【发布时间】:2016-10-18 14:15:48
【问题描述】:
我有一个由邻接列表描述的层次结构。不一定有单个根元素,但我确实有数据来识别层次结构中的叶(终端)项目。所以,一个看起来像这样的层次结构......
1
- 2
- - 4
- - - 7
- 3
- - 5
- - 6
8
- 9
... 会用一个表格来描述,就像这样。 注意:我无法更改此格式。
id parentid isleaf
--- -------- ------
1 null 0
2 1 0
3 1 0
4 2 0
5 3 1
6 3 1
7 4 1
8 null 0
9 8 1
这里是示例表定义和数据:
CREATE TABLE [dbo].[HiearchyTest](
[id] [int] NOT NULL,
[parentid] [int] NULL,
[isleaf] [bit] NOT NULL
)
GO
INSERT [dbo].[HiearchyTest] ([id], [parentid], [isleaf]) VALUES (1, NULL, 0)
INSERT [dbo].[HiearchyTest] ([id], [parentid], [isleaf]) VALUES (2, 1, 0)
INSERT [dbo].[HiearchyTest] ([id], [parentid], [isleaf]) VALUES (3, 1, 0)
INSERT [dbo].[HiearchyTest] ([id], [parentid], [isleaf]) VALUES (4, 2, 0)
INSERT [dbo].[HiearchyTest] ([id], [parentid], [isleaf]) VALUES (5, 3, 1)
INSERT [dbo].[HiearchyTest] ([id], [parentid], [isleaf]) VALUES (6, 3, 1)
INSERT [dbo].[HiearchyTest] ([id], [parentid], [isleaf]) VALUES (7, 4, 1)
INSERT [dbo].[HiearchyTest] ([id], [parentid], [isleaf]) VALUES (8, NULL, 0)
INSERT [dbo].[HiearchyTest] ([id], [parentid], [isleaf]) VALUES (9, 8, 1)
GO
据此,我需要提供任何 id 并获取所有祖先的列表,包括每个祖先的所有后代。因此,如果我提供 id = 6 的输入,我会期望以下内容:
id descendentid
-- ------------
1 1
1 3
1 6
3 3
3 6
6 6
- id 6 有它自己
- 它的父代 id 3 的后代是 3 和 6
- 其父代 id 1 的后代为 1、3 和 6
我将使用这些数据在层次结构中的每个级别提供汇总计算。这很好用,假设我可以得到上面的数据集。
我已经使用两个 recusive ctes 完成了这个 - 一个用于获取层次结构中每个节点的“终端”项。然后,第二个我得到我选择的节点的完整祖先(所以,6 解析为 6、3、1)走上去得到完整的集合。我希望我遗漏了一些东西,并且这可以在一轮中完成。这是示例双递归代码:
declare @test int = 6;
with cte as (
-- leaf nodes
select id, parentid, id as terminalid
from HiearchyTest
where isleaf = 1
union all
-- walk up - preserve "terminal" item for all levels
select h.id, h.parentid, c.terminalid
from HiearchyTest as h
inner join
cte as c on h.id = c.parentid
)
, cte2 as (
-- get all ancestors of our test value
select id, parentid, id as descendentid
from cte
where terminalid = @test
union all
-- and walkup from each to complete the set
select h.id, h.parentid, c.descendentid
from HiearchyTest h
inner join cte2 as c on h.id = c.parentid
)
-- final selection - order by is just for readability of this example
select id, descendentid
from cte2
order by id, descendentid
附加细节:“真实”层次结构将比示例大得多。从技术上讲,它可以有无限的深度,但实际上它很少会超过 10 层。
总而言之,我的问题是我是否可以通过单个递归 cte 来完成此操作,而不必在层次结构上递归两次。
【问题讨论】:
-
msdn.microsoft.com/en-us/library/bb677173.aspx 这是微软关于一些新的层次结构函数的文章,正是针对这一点的。
-
@Matt - 感谢您的建议,但我上面的注释指出我无法更改数据结构(它不是我的表)。所以,我没有能力添加一个 hierarchyid 列。此外,根据我对层次结构 ID 的经验,我还没有看到一种方法可以满足我的要求。您会注意到,根据我输入的 id = 6,我需要该 id 的所有祖先的完整后代列表。
-
知道了,我读得很快 :) 递归 cte 很可能是我会再读一遍的路线,看看它是否能帮上忙。
-
我认为你有正确的方法,但有兴趣看看是否有人有一些巧妙的解决方案来绕过两个递归 cte。如果这是一项足够常见且足够昂贵的任务,您可以为每条记录预先构建一个新表,但这可能不可行。
-
如果您只是在第一个 CTE 中寻找简单的东西,而不是“保留”终端 ID,为什么不保留路径?例如
cast(id as varchar(4000)) as teminalid在 cte 的第一部分,然后在它的第二部分cast(h.id as varchar(4000)) + '/' + cast(c.terminalid as varchar(4000))。或者这个想法的一个变种,取决于你的需要。之后,您只需要在第一个 cte 中检查带有charindex('6', terminalid) > 0的任何内容,并且您将拥有每个人的完整路径,除非我遗漏了什么。
标签: sql sql-server sql-server-2014 common-table-expression hierarchical-data