Иерархическое ПОРЯДОК Дерева и Дитя


21

Я должен следующие данные в SQL Server 2008 R2. SQLFiddle

Схема:

CREATE TABLE [dbo]. [ICFilters] (
   [ICFilterID] [int] IDENTITY (1,1) НЕ NULL,
   [ParentID] [int] НЕ NULL ПО УМОЛЧАНИЮ 0,
   [FilterDesc] [varchar] (50) NOT NULL,
   [Активный] [tinyint] НЕ NULL ПО УМОЛЧАНИЮ 1,
 CONSTRAINT [PK_ICFilters] ПЕРВИЧНЫЙ КЛЮЧ КЛАСТЕР 
 ([ICFilterID] ASC) С 
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    IGNORE_DUP_KEY = OFF,
    ALLOW_ROW_LOCKS = ON,
    ALLOW_PAGE_LOCKS = ON
 ) НА [ПЕРВИЧНО]
) НА [ПЕРВИЧНО]

INSERT INTO [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 | PARENTID | FILTERDESC | ACTIVE |
--------------------------------------------------
| 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 | PARENTID | FILTERDESC | ACTIVE |
--------------------------------------------------
| 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 () и partition by для создания «пути», который включает желаемую сортировку. 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
Это именно то, что мне было нужно! Я согласен, слишком много клеток мозга умерли от этого. Разве я не понял, чего хотел? Если так, я отредактирую вопрос для дальнейшего использования. Я определенно делал это тяжелее, чем нужно было ...
Archangel33

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

Выше, кажется, не работает правильно для меня. Представьте себе 2 таблицы настройки с типом данных facebook. Таблица 1, имеет PostId + другие поля. PostId - это автоинкремент, и, очевидно, в вашем интерфейсе вы будете сортировать DESC, чтобы иметь самый последний пост в верхней части.

Теперь для таблицы комментариев. Таблица 2 Эта таблица CommentId является первичным ключом, автоматическим номером. В вашем графическом интерфейсе вы хотите отобразить его 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 было два комментария (CommentIds 1 и 2). Затем кто-то ответил на Comment1 (CommentId 5, ParentId 1), а затем кто-то прокомментировал этот ответ, так что Comment5 (CommentId 6, ParentId 6)

И альт, последовательность правильная, под постом теперь можно показывать комментарии в правильной последовательности. Чтобы сделать отступ для постов так, чтобы они формировались и обрисовывались, как в Facebook (чем глубже уровень, тем больше он должен быть слева), у меня также есть столбец «Отступ». Корни равны 0, а затем в объединении мы имеем c.Indent + 1 AS Отступ В коде yo теперь может умножить отступ с помощью предположения 32px и показать комментарии в хорошей иерархии и набросках.

Я не вижу проблем с использованием первичного ключа с автоинкрементом CommentId в качестве движущей силы для создания моего SortKey, поскольку лучше изменить время, когда вы путаете даты (commentdate), чем путать ключ, управляемый базой данных, который начинается с +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.