SQL сложный рекурсивный CTE

Я работаю в SQL Server 2014. У меня странная ситуация с иерархией данных. (По крайней мере, я не испытывал ничего подобного раньше.)

В моей иерархии есть несколько объектов корневого / высшего уровня. Каждый объект ниже корневого уровня отображается на один и только один объект над ним. Не каждый путь узла имеет одинаковую длину. Например, один путь может содержать 2 уровня объекта, а другой путь может содержать 20 уровней объекта.

Каждый объект в иерархии имеет IsInherited атрибут, а также некоторые другие атрибуты (назовите его SomeAttribute). IsInherited атрибут указывает, наследует ли данный объект значение SomeAttribute от его самого непосредственного родителя. Естественно, если объект IsInherited атрибут "Y", то данный объект наследует значение SomeAttribute от самого непосредственного родителя (который, в свою очередь, может наследовать его значение для самого непосредственного родителя и т. д.). В противном случае объект SomeAttribute значение указано.

Теперь все вышеперечисленное не обязательно является редкостью. Что делает эту ситуацию необычной, так это конкретная реализация этого наследования. Эта иерархия хранится в одной таблице (делает ли это модель списка смежности?), А значение SomeAttribute не заполняется для данного объекта / строки, если его IsInherited Атрибут "Y".

Моя цель - вернуть ценность SomeAttribute для всех объектов в иерархии.

Пример таблицы выглядит следующим образом:

CREATE TABLE hierarchy (
    ID int NOT NULL
    ,ParentID int NULL
    ,SomeAttribute char(1) NULL
    ,IsInherited char(1) NOT NULL
)
;
INSERT INTO hierarchy (ID, ParentID, SomeAttribute, IsInherited)
VALUES
(1, NULL, 'a', 'N')
,(2, NULL, 'b', 'N')
,(3, NULL, 'c', 'N')
,(4, NULL, 'd', 'N')
,(5, NULL, 'e', 'N')
,(6, NULL, 'f', 'N')
,(7, NULL, 'g', 'N')
,(8, 2, NULL, 'Y')
,(9, 3, 'h', 'N')
,(10, 4, NULL, 'Y')
,(11, 5, 'j', 'N')
,(12, 6, NULL, 'Y')
,(13, 6, 'k', 'N')
,(14, 7, 'l', 'N')
,(15, 7, 'm', 'N')
,(16, 10, NULL, 'Y')
,(17, 11, NULL, 'Y')
,(18, 16, NULL, 'Y')
,(19, 17, NULL, 'Y')
,(20, 19, 'o', 'N')
;

Это дает нам следующие пути к узлам:

1
2-8
3-9
4-10-16-18
5-11-17-19-20
6-12,13
7-14,15

Итак, с этим образцом таблицы я ожидаю возврата:

ID    SomeAttribute
1     a
2     b
3     c
4     d
5     e
6     f
7     g
8     b (inherited from 2)
9     h
10    d (inherited from 4)
11    j
12    f (inherited from 6)
13    k
14    l
15    m
16    d (inherited from 10, inherited from 4)
17    j (inherited from 11)
18    d (inherited from 16, inherited from 10, inherited from 4)
19    j (inherited from 17, inherited from 11)
20    o

Я знаю, что это, вероятно, требует рекурсивных CTE. Я пытаюсь написать SQL для этого. Как мне вернуть желаемый результат?

1 ответ

Решение

Это модель списка смежности, потому что каждая строка представляет пару смежных узлов.

И рекурсивный CTE будет выглядеть так:

 with q as
(
  select id, SomeAttribute, cast(id as varchar(max)) SomeAttributePath
  from hierarchy
  where ParentID is null
  union all
  select c.id, case when c.IsInherited = 'Y' then q.SomeAttribute else c.SomeAttribute end as SomeAttribute, cast(concat(q.SomeAttributePath,'-',c.id) as varchar(max))
  from q
  join hierarchy c
  on c.ParentID = q.ID
)
select *
from q
order by id

Outputing:

id          SomeAttribute SomeAttributePath
----------- ------------- -----------------
1           a             1
2           b             2
3           c             3
4           d             4
5           e             5
6           f             6
7           g             7
8           b             2-8
9           h             3-9
10          d             4-10
11          j             5-11
12          f             6-12
13          k             6-13
14          l             7-14
15          m             7-15
16          d             4-10-16
17          j             5-11-17
18          d             4-10-16-18
19          j             5-11-17-19
20          o             5-11-17-19-20
Другие вопросы по тегам