SQL Server中的多对多递归CTE

时间:2018-08-23 14:01:24

标签: sql-server recursion many-to-many recursive-cte

据我了解,如果将一个零件定义为父零件而将另一个零件定义为子零件,那么多对多关系就是一个层次结构。

在某些情况下,我需要在多对多表中获取映射到自身的对象的子对象,但是我很难为其编写递归CTE。

例如,假设我有:

ParentId           ChildId
========           =======
2                  20
2                  30
5                  50
20                 200
21                 201
30                 300
31                 301

对于根级别,我使用的规则是,如果它的ID不在第二列(ChildId)中,则它是一个根对象,但这是有问题的,因为如上例所示,根的多个记录(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

2 个答案:

答案 0 :(得分:0)

这只是一个典型的递归cte,到处都有成千上万个示例。这将返回我认为您想要的。

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

答案 1 :(得分:0)

这里的答案基本上与肖恩·兰格相同。但是由于我已经准备好了,所以我还是想将其发布。

;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