#ssms #hierarchical-data #recursive-cte
#ssms #иерархические-данные #recursive-cte
Вопрос:
Я создаю запрос, который выводит иерархию прав собственности для каждого корня в моей базе данных. Я успешно использую рекурсивный CTE, поскольку в настоящее время я могу получить следующие выходные данные:
rootID RootName RelatedName
1 ABA GPS
1 ABA PIG
1 ABA BBY
1 ABA PIG
2 PIG DDS
2 PIG GPS
Чего я пытаюсь достичь, так это столбца идентификатора группы, в котором данные могут выглядеть следующим образом:
GroupID rootID RootName RelatedName
100 1 ABA GPS
100 1 ABA PIG
100 1 ABA BBY
100 1 ABA PIG
100 2 PIG DDS
100 2 PIG GPS
и аналогично для группы 200, 300, … и т.д. для каждого дерева. Какую часть рекурсивного CTE можно ввести в код для достижения вышеуказанного результата?
;WITH cte_Rel AS (
SELECT
<columns>
FROM #RawRel r
WHERE 1 = 1
AND <initial Conditions>
UNION ALL
SELECT
<Columns>
FROM #RawRel r
JOIN cte_Rel c ON r.RootName = c.RelatedName
)
SELECT DISTINCT * FROM cte_Rel
OPTION (MAXRECURSION 100)
Комментарии:
1. Откуда берется значение
GroupID
? Поскольку это одно и то же значение, почему не просто100 AS GroupID
?2. В моих данных будет несколько деревьев… Итак, в приведенной выше демонстрации CTE сгенерирует это дерево 100, но также могут быть и другие деревья, для которых потребуется собственный groupId.
3. Поэтому мне понадобится умная стратегия увеличения в этом CTE или что-то в этом роде…
Ответ №1:
Вы можете добавить номер строки в привязочную часть рекурсивного CTE. Умножьте на 100 и повторите тот же столбец во второй части CTE.
Скрипка на случай, если вы предпочитаете интерактивный код.
Пример данных
Без вашего фактического запроса и выборки входных данных сложно полностью воспроизвести ваш текущий вывод, поэтому я сгенерировал свои собственные выборки данных.
create table RelData
(
ParentId int,
Id int,
Name nvarchar(3)
);
insert into RelData (ParentId, Id, Name) values
(null, 1, 'A00'), -- tree A
(1, 2, 'A10'),
(2, 3, 'A11'),
(2, 4, 'A12'),
(1, 5, 'A20'),
(5, 6, 'A21'),
(null, 7, 'B00'), -- tree B
(7, 8, 'B10'),
(8, 9, 'B11');
Решение
WITH cte_Rel AS (
SELECT row_number() over(order by rd.Id) * 100 as TreeId, -- number to roots and multiply the root number by 100
rd.Id, rd.Name, rd.ParentId, convert(nvarchar(3), null) as ParentName
FROM RelData rd
WHERE rd.ParentId is null
UNION ALL
SELECT c.TreeId, -- repeat the tree number
rd.Id, rd.Name, rd.ParentId, c.name
FROM RelData rd
JOIN cte_Rel c ON rd.ParentId = c.Id
)
SELECT c.TreeId, c.ParentId, c.ParentName, c.Name
FROM cte_Rel c
where c.ParentId is not null
order by c.ParentId;
Результат
TreeId ParentId ParentName Name
------ -------- ---------- ----
100 1 A00 A10
100 1 A00 A20
100 2 A10 A11
100 2 A10 A12
100 5 A20 A21
200 7 B00 B10
200 8 B10 B11