2017-06-23 272 views
2

我在SQL Server 2014中工作。我有一个奇怪的数据层次结构情况。 (至少,我以前没有遇到类似的东西。)SQL复杂的递归CTE

在我的层次结构中,有几个根/最高级别的对象。根级别下的每个对象都映射到其上的一个且仅有一个对象。并非每个节点路径都是相同的长度。例如,一条路径可以包含2个对象层次,而另一条路径可以包含20个对象层次。

层次结构中的每个对象都有一个IsInherited属性以及一些其他属性(称为SomeAttribute)。 IsInherited属性指示给定对象是否从最直接的父项继承SomeAttribute的值。当然,如果一个对象的IsInherited属性为'Y',那么给定的对象从其最直接的父对象继承值SomeAttribute(这可能会继承其最直接父对象的值,等等)。否则,指定对象的值为SomeAttribute

现在,以上所有都不一定非同寻常。这种情况不常见的是这种继承的具体实现。此层次结构存储在单个表中(这是否使其成为邻接列表模型?),并且如果其IsInherited属性为'Y',则为给定对象/行填充SomeAttribute的值为而非

我的目标是返回层次结构中所有对象的值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。我如何返回我想要的输出?

回答

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 
+1

我不得不把投在你的CONCAT,使查询运行。更重要的是,SomeAttribute中的一些值是错误的。例如,20应该是o,而不是e,因为它的IsInherited标志是N;同样,9应该是h。 – user3100444

+0

@ user3100444谢谢。我意外地拒绝了你的编辑,所以手工应用了它的变化。 –

+1

如果您想查看属性的路径,仅限于实际从中获取属性的路径,则还可以为第三列使用一个案例表达式。 'CASE当C.IsInherited ='Y'THEN q.SomeAttributePath +' - 'ELSE''END + CAST(c.id AS VARCHAR(MAX))' –