SQL 服务器中的多对多递归 CTE
Many-to-many to recursive CTE in SQL Server
据我了解,如果您将一个部分定义为父部分,将另一个部分定义为子部分,则任何多对多关系都是层次结构。
我遇到这样一种情况,我需要在多对多 table 中获取一个映射到自身的对象的子对象,但我很难为它编写递归 CTE .
例如,假设我有:
ParentId ChildId
======== =======
2 20
2 30
5 50
20 200
21 201
30 300
31 301
对于根级别,我使用的规则是,如果它的 ID 不在第二列 (ChildId) 中,它就是一个根对象,但这是有问题的,因为正如您从上面的示例中看到的那样,我root (Id: 2) 可以有多个记录
对于递归部分,无论我尝试什么,要么得到无限递归,要么递归根本不起作用。
所以我完全卡住了。
更新
只是添加更多说明。我正在构建一个存储过程,该过程在 parentId 中传递并且内部具有递归 CTE。我试图获得的输出是,对于传入 2 的 parentId,使用上面的示例,我想要:
ParentId ChildId Level
======== ======= =====
2 20 0
2 30 0
20 200 1
30 300 1
这是我的一些代码:
alter procedure [dbo].[FindChildren]
@Id bigint
as
begin
;with DocumentHierarchyCTE (ParentId, ChildId, LevelNumber)
as
(
select
th.ParentDocumentId as ParentId,
th.ChildDocumentId as ChildId,
0 as LevelNumber
from dbo.DocumentHierarchy th
where
th.ParentDocumentId = @Id
and not exists(select 1 from dbo.DocumentHierarchy tz where tz.ChildDocumentId = th.ParentDocumentId )
union all
???
)
select *
from DocumentHierarchyCTE d
option (maxrecursion 0)
end
go
这只是一个典型的递归 cte,到处都有成千上万的例子。这 returns 我想你想要的。
declare @Something table
(
ParentId int
, ChildId int
)
insert @Something values
(2, 20)
, (2, 30)
, (5, 50)
, (20, 200)
, (21, 201)
, (30, 300)
, (31, 301)
declare @ParentID int = 2
;
with MyCTE as
(
select *, MyLevel = 0
from @Something s
where s.ParentId = @ParentID
AND ParentID not in
(
Select s2.ChildId
from @Something s2
)
union all
select s3.*, c.MyLevel + 1
from @Something s3
join MyCTE c on c.ChildId = s3.ParentId
)
select *
from MyCTE
这里基本上和Sean Lange的回答是一样的。但是既然我已经准备好了,我想我还是 post 它。
;WITH list_structure AS
(
SELECT
l.parentID,
l.childID,
0 AS levels
FROM list_hierarchy l
WHERE l.parentID NOT IN (SELECT childID FROM list_hierarchy)
UNION ALL
SELECT
l.parentID,
l.childID,
levels+1
FROM list_hierarchy l
INNER JOIN list_structure
ON l.parentID = list_structure.childID
)
SELECT * FROM list_structure
据我了解,如果您将一个部分定义为父部分,将另一个部分定义为子部分,则任何多对多关系都是层次结构。
我遇到这样一种情况,我需要在多对多 table 中获取一个映射到自身的对象的子对象,但我很难为它编写递归 CTE .
例如,假设我有:
ParentId ChildId
======== =======
2 20
2 30
5 50
20 200
21 201
30 300
31 301
对于根级别,我使用的规则是,如果它的 ID 不在第二列 (ChildId) 中,它就是一个根对象,但这是有问题的,因为正如您从上面的示例中看到的那样,我root (Id: 2) 可以有多个记录
对于递归部分,无论我尝试什么,要么得到无限递归,要么递归根本不起作用。
所以我完全卡住了。
更新 只是添加更多说明。我正在构建一个存储过程,该过程在 parentId 中传递并且内部具有递归 CTE。我试图获得的输出是,对于传入 2 的 parentId,使用上面的示例,我想要:
ParentId ChildId Level
======== ======= =====
2 20 0
2 30 0
20 200 1
30 300 1
这是我的一些代码:
alter procedure [dbo].[FindChildren]
@Id bigint
as
begin
;with DocumentHierarchyCTE (ParentId, ChildId, LevelNumber)
as
(
select
th.ParentDocumentId as ParentId,
th.ChildDocumentId as ChildId,
0 as LevelNumber
from dbo.DocumentHierarchy th
where
th.ParentDocumentId = @Id
and not exists(select 1 from dbo.DocumentHierarchy tz where tz.ChildDocumentId = th.ParentDocumentId )
union all
???
)
select *
from DocumentHierarchyCTE d
option (maxrecursion 0)
end
go
这只是一个典型的递归 cte,到处都有成千上万的例子。这 returns 我想你想要的。
declare @Something table
(
ParentId int
, ChildId int
)
insert @Something values
(2, 20)
, (2, 30)
, (5, 50)
, (20, 200)
, (21, 201)
, (30, 300)
, (31, 301)
declare @ParentID int = 2
;
with MyCTE as
(
select *, MyLevel = 0
from @Something s
where s.ParentId = @ParentID
AND ParentID not in
(
Select s2.ChildId
from @Something s2
)
union all
select s3.*, c.MyLevel + 1
from @Something s3
join MyCTE c on c.ChildId = s3.ParentId
)
select *
from MyCTE
这里基本上和Sean Lange的回答是一样的。但是既然我已经准备好了,我想我还是 post 它。
;WITH list_structure AS
(
SELECT
l.parentID,
l.childID,
0 AS levels
FROM list_hierarchy l
WHERE l.parentID NOT IN (SELECT childID FROM list_hierarchy)
UNION ALL
SELECT
l.parentID,
l.childID,
levels+1
FROM list_hierarchy l
INNER JOIN list_structure
ON l.parentID = list_structure.childID
)
SELECT * FROM list_structure