Ієрархічне замовлення дерева-батька


21

Я повинен слідкувати за даними в SQL Server 2008 R2. SQLFiddle

Схема:

СТВОРИТИ ТАБЛИЦЮ [dbo]. [ICFilters] (
   [ICFilterID] [int] ІДЕНТИЧНІСТЬ (1,1) NOT NULL,
   [ParentID] [int] NOT NULL DEFAULT 0,
   [FilterDesc] [varchar] (50) НЕ NULL,
   [Активний] [крихітний] НЕ ВІДПОВІДНИЙ 1,
 CONSTRAINT [PK_ICFilters] PRIMARY KEY CLUSTERED 
 ([ICFilterID] ASC) З 
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    IGNORE_DUP_KEY = OFF,
    ALLOW_ROW_LOCKS = УВІМКНЕНО,
    ALLOW_PAGE_LOCKS = УВІМКНЕНО
 ) ON [ПЕРШИЙ]
) ON [ПЕРШИЙ]

ВСТАВИТИ В [dbo]. [ICFilters] (ParentID, FilterDesc, Active)
Цінності 
(0, "Тип продукту", 1),
(1, "ProdSubType_1", 1),
(1, "ProdSubType_2", 1),
(1, "ProdSubType_3", 1),
(1, "ProdSubType_4", 1),
(2, "PST_1.1", 1),
(2, "PST_1.2", 1),
(2, "PST_1.3", 1),
(2, "PST_1.4", 1),
(2, "PST_1.5", 1),
(2, "PST_1.6", 1),
(2, "PST_1.7", 0),
(3, "PST_2.1", 1),
(3, "PST_2.2", 0),
(3, "PST_2.3", 1),
(3, "PST_2.4", 1),
(14, "PST_2.2.1", 1),
(14, "PST_2.2.2", 1),
(14, "PST_2.2.3", 1),
(3, "PST_2.8", 1)

Таблиця:

| ICFILTERID | ПАРЕНТИД | FILTERDESC | АКТИВНІ |
--------------------------------------------------
| 1 | 0 | Тип продукту | 1 |
| 2 | 1 | ProdSubType_1 | 1 |
| 3 | 1 | ProdSubType_2 | 1 |
| 4 | 1 | ProdSubType_3 | 1 |
| 5 | 1 | ProdSubType_4 | 1 |
| 6 | 2 | PST_1.1 | 1 |
| 7 | 2 | PST_1.2 | 1 |
| 8 | 2 | PST_1.3 | 1 |
| 9 | 2 | PST_1.4 | 1 |
| 10 | 2 | PST_1.5 | 1 |
| 11 | 2 | PST_1.6 | 1 |
| 12 | 2 | PST_1.7 | 0 |
| 13 | 3 | PST_2.1 | 1 |
| 14 | 3 | PST_2.2 | 0 |
| 15 | 3 | PST_2.3 | 1 |
| 16 | 3 | PST_2.4 | 1 |
| 17 | 14 | PST_2.2.1 | 1 |
| 18 | 14 | PST_2.2.2 | 1 |
| 19 | 14 | PST_2.2.3 | 1 |
| 20 | 3 | PST_2.8 | 1 |

Кожен рядок має ідентифікатор свого батьківського та кореневого parentid = 0. Це FilterDescлише описи зразків, тому я не можу спробувати розібрати їх для замовлення.

Питання

Чи можна виділити всі рядки у вигляді дерева? Якщо так, то як? Коли я кажу "деревоподібний", я маю на увазі рекурсивно вибирати батьків, за якими слідують усі його діти, потім усі діти кожного з них тощо. Глибина першого обходу дерева.

Ми з Моїми друзями намагалися, але ми не вистачали робочих рішень, але намагаємось продовжувати. Я досить новий в sql, тому, можливо, це можна зробити легко, і я просто роблю речі складніше, ніж потрібно.

Приклад (бажаний) вихід:

| ICFILTERID | ПАРЕНТИД | FILTERDESC | АКТИВНІ |
--------------------------------------------------
| 1 | 0 | Тип продукту | 1 |
| 2 | 1 | ProdSubType_1 | 1 |
| 6 | 2 | PST_1.1 | 1 |
| 7 | 2 | PST_1.2 | 1 |
| 8 | 2 | PST_1.3 | 1 |
| 9 | 2 | PST_1.4 | 1 |
| 10 | 2 | PST_1.5 | 1 |
| 11 | 2 | PST_1.6 | 1 |
| 12 | 2 | PST_1.7 | 0 |
| 3 | 1 | ProdSubType_2 | 1 |
| 13 | 3 | PST_2.1 | 1 |
| 14 | 3 | PST_2.2 | 0 |
| 17 | 14 | PST_2.2.1 | 1 |
| 18 | 14 | PST_2.2.2 | 1 |
| 19 | 14 | PST_2.2.3 | 1 |
| 15 | 3 | PST_2.3 | 1 |
| 16 | 3 | PST_2.4 | 1 |
| 20 | 3 | PST_2.8 | 1 |
| 4 | 1 | ProdSubType_3 | 1 |
| 5 | 1 | ProdSubType_4 | 1 |

Найкраще використовувати CTE
Кін Шах

1
Ось нитка, що показує бажане сортування результатів, не маючи завантажувати дані таблиці в будь-якому конкретному порядку. Він використовує row_number () та розділ, щоб створити "шлях", який забезпечує бажане сортування. ask.sqlservercentral.com/questions/48518/…

Відповіді:


25

Гаразд, достатньо клітин мозку мертві.

SQL Fiddle

WITH cte AS
(
  SELECT 
    [ICFilterID], 
    [ParentID],
    [FilterDesc],
    [Active],
    CAST(0 AS varbinary(max)) AS Level
  FROM [dbo].[ICFilters]
  WHERE [ParentID] = 0
  UNION ALL
  SELECT 
    i.[ICFilterID], 
    i.[ParentID],
    i.[FilterDesc],
    i.[Active],  
    Level + CAST(i.[ICFilterID] AS varbinary(max)) AS Level
  FROM [dbo].[ICFilters] i
  INNER JOIN cte c
    ON c.[ICFilterID] = i.[ParentID]
)

SELECT 
  [ICFilterID], 
  [ParentID],
  [FilterDesc],
  [Active]
FROM cte
ORDER BY [Level];

2
Це саме те, що мені було потрібно! Я погоджуюсь, що надто багато клітин мозку загинули від цього. Невже я не зрозумів, чого хотів? Якщо так, я відредагую це питання для подальшого ознайомлення. Я, безумовно, робив це складніше, ніж потрібно було ...
Архангел33,

1
@ Archangel33 Ви зробили хорошу роботу, виклавши питання і що вам потрібно. Плюс, sqlfiddle дійсно допомагають.
Тревіс

2
+1, але використання [ICFilterID] [int] IDENTITY (1,1) для сортування буде працювати лише в тому, що елементи вставляються у правильній послідовності, але інше поле для сортування ще не реалізовано OT
bummi

4
Я не вірю, що це 100% правильне рішення. Хоча він перераховує всі рядки з їх правильним рівнем в ієрархії, він не перераховує їх у тому порядку, у якому запитання їх запитувало. Чи можна було б перерахувати рядки у правильному порядку відповідно до питання? Це я теж шукаю.

1
Це дійсно відповідає на моє запитання, оскільки надані дані у [FilterDesc]стовпці є фіктивними, і що наказ є непотрібним / неважливим. Дотримуючись логіки відповіді @Travis Gan, все, що потрібно зробити, щоб отримати це впорядкування, - це додати ще одне CASTдо Level. напр. Level + CAST( CAST(i.[ICFilterID] AS varbinary(max)) AS LevelСтає Level + CAST(i.[FilterDesc] AS varbinary(max)) + CAST(i.[ICFilterID] AS varbinary(max)) AS Level.
Архангел33

1

Сказане, здається, для мене не працює правильно. Уявіть налаштування двох таблиць із типом даних у Facebook. У таблиці 1 є PostId + you інші поля. PostId є автоматичним збільшенням, і, очевидно, у вашому інтерфейсі ви відсортуєте DESC, щоб остання публікація була вгорі.

Тепер для таблиці коментарів. Таблиця 2 Ця таблиця CommentId - це первинний ключ, номер авто. У вашому gui, ви хочете відобразити його ASC, так що, читаючи нитку, це має сенс. (найдавніша (менша кількість) вгорі) Іншими важливими клавішами таблиці 2 є: PostId (FK назад до публікацій) та ParentId (FK до CommentId), де ParentId буде NULL, якщо це "кореневий" коментар до повідомлення. Якщо хтось ВІДПОВІДАЄ на коментар, то parentId буде заповнений коментованим.
Сподіваюся, ви, хлопці, отримаєте дрейф. CTE буде виглядати приблизно так:

WITH  Comments
        AS ( SELECT  CommentId , ParentId, CAST(CommentId AS VARBINARY(MAX)) AS Sortkey, 0 AS Indent
             FROM    dbo.Comments
             WHERE   ParentId IS NULL AND PostId = 105
             UNION ALL
             SELECT  b.CommentId , b.ParentId,  c.Sortkey + CAST(b.CommentId AS varbinary(max))  AS Sortkey, c.Indent + 1 AS Indent
             FROM    dbo.Comments b
             INNER JOIN Comments c ON c.CommentId = b.ParentId
           )
   SELECT   *
   FROM     Comments
   ORDER BY Sortkey

Вибірка зразка

1   NULL    0x0000000000000001  0
5   1   0x00000000000000010000000000000001  1
6   5   0x000000000000000100000000000000010000000000000005  2
2   NULL    0x0000000000000002  0

У публікації F / B 105 було два коментарі (коментарі 1 та 2). Хтось потім відповів на коментар1 (коментар 5, батьківський 1), а потім хтось прокоментував цю відповідь, так що в коментарі5 (коментар 6, батьків 6)

І віола, послідовність правильна, під публікацією тепер ви можете показувати коментарі в потрібній послідовності. Щоб відступати від публікацій, щоб вони формувалися та окреслювались, як у фейсбуці (чим глибше йде рівень, тим більше він повинен бути відмічений зліва), у мене також є стовпець під назвою «Відступ». Коріння дорівнюють 0, а потім у об'єднанні ми маємо c.Indent + 1 AS indent У коді, тепер ви можете помножити відступ на припущення 32px та показувати коментарі в приємній ієрархії та окресленні.

Я не бачу жодних проблем із використанням первинного ключа автоматичного примноження CommentId як рушійної сили для створення мого SortKey, оскільки краща зміна ваших дат пошкодження (коментар) є кращою, ніж замішання ключа, керованого базою даних, що насідає +1


0
create table pc ( parent varchar(10), child varchar(10) )

insert into pc values('a','b');
insert into pc values('a','c');
insert into pc values('b','e');
insert into pc values('b','f');
insert into pc values('a','d');
Insert into pc values('b','g');
insert into pc values('c','h');
insert into pc values('c','i');
insert into pc values('d','j');
insert into pc values('f','k');
insert into pc values('x','y');
insert into pc values('y','z');
insert into pc values('m','n');

 DECLARE @parent varchar(10) = 'a';
 WITH cte AS
 (
  select null parent, @parent child, 0 as level
   union
  SELECT  a.parent, a.child , 1 as level
    FROM pc a
   WHERE a.parent = @parent
   UNION ALL
  SELECT a.parent, a.child , c.level +    1
  FROM pc a JOIN cte c ON a.parent = c.child
  )
  SELECT distinct parent, child , level
  FROM cte
  order by level, parent

Це дасть вам всі нащадки та рівень.
Сподіваюся, це допомагає :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.