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