SQL复杂的递归CTE

时间:2017-06-23 20:07:44

标签: sql-server adjacency-list-model

我在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 个答案:

答案 0 :(得分:4)

这是邻接列表模型,因为每行代表一对相邻节点。

递归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