【问题标题】:Many-to-many to recursive CTE in SQL ServerSQL Server 中的多对多到递归 CTE
【发布时间】:2018-08-23 14:01:24
【问题描述】:

据我了解,如果您将一个部分定义为父级,另一个定义为子级,则任何多对多关系都是一种层次结构。

我有一种情况,我需要在多对多表中获取映射到自身的对象的子对象,但我很难为它编写递归 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

【问题讨论】:

  • 您好,欢迎来到 SO。你说递归根本不起作用。这意味着什么?你能分享你试过的吗?此外,您的示例数据有点混乱(而且似乎不完整)。这将是一个很好的起点。 spaghettidba.com/2015/04/24/…
  • 为什么样本数据令人困惑? 2个有孩子(20,30),20个有孩子(200),30个有孩子(300)。当我说递归不起作用时,我的意思是递归只返回根记录。
  • 你希望你的输出是什么样的?
  • 嗯...200 和 300 在哪里?你怎么知道 2 是你的根?递归 cte 的想法是从根开始并找到所有孩子。您无法在这些数据中知道根是什么。再说一次,你尝试了什么?我的意思是分享您尝试但不起作用的查询。给我们一些可以合作的东西。
  • @RobertSievers:我更新了我的答案

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


【解决方案1】:

这只是一个典型的递归 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

【讨论】:

  • 哇,就是这么简单 :) 我把它复杂化了。谢谢
【解决方案2】:

这里的答案与 Sean Lange 基本相同。但是既然我已经准备好了,我想我还是会发布它。

;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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-20
    • 1970-01-01
    • 2012-04-23
    • 2014-05-19
    • 1970-01-01
    • 2020-06-08
    • 2012-12-25
    • 1970-01-01
    相关资源
    最近更新 更多