【问题标题】:Recursive CTE to return all connected Nodes in Path递归 CTE 返回路径中所有连接的节点
【发布时间】:2017-08-01 20:21:56
【问题描述】:

我有一个表,它通过将两个“节点”链接在一起来创建路径。

Node
-----------
Id - Primary Key
Name 
etc...


Path
------
Id - Primary Key
From - FK to Node
To - FK to Node

所以这条路:

W --- X --- Y --- Z 

可以这样构建:

Node
Id   Name
---  -----
1    W
2    X
3    Y
4    Z
5    A
6    B
7    C


Path
Id  From     To
--- -------  -------
1   1        2
2   2        3
3   3        4
4   6        7

我想出了一个递归 CTE 查询,它给定任何节点 ID,遍历路径并返回所有涉及的“路径”。

declare @nodeId = 2
;WITH cte AS (

    -- ANCHOR
    -- Find one path involving Node
    SELECT top 1 p.*, 0 as [Seq] FROM dbo.Path p WHERE [From] = @nodeId or [To] = @nodeId 

    union all

    -- go left
    select leftPath.*, cte.[Seq] - 1 as [Seq]
    from [Path] leftPath
    join cte on cte.[From] = leftPath.[To] and cte.[Seq] <= 0

    union all

    -- go right
    select rightPath.*, cte.[Seq] + 1 as [Seq]
    from [Path] rightPath
    join cte on cte.[To] = rightPath.[From] and cte.[Seq] >= 0
)

SELECT cte.Id, cte.Seq, cte.From, cte.To
FROM cte 
order by [Seq]

所以这会返回所有路径 -

Path
Id  Seq From     To
--- --- -------  -------
1   -1   1        2
2   0    2        3
3   1    3        4

但是如何编写一个给定节点 ID 的查询,遍历左右路径并返回所涉及的所有 DISTINCT(有序)节点?

    Id   Name
    ---  -----
    1    W
    2    X
    3    Y
    4    Z

【问题讨论】:

  • 您的查询似乎不起作用,例如description 不存在。请解决这个问题,我会看看rextester.com/DEVHNG47817
  • 已修复;见:rextester.com/live/QBDQ1655
  • 该查询与您的输出不匹配。不否定Seq
  • 有一个问题,您共享一个LIVE 代码,所以当我编辑更改代码时。使用底部的FORK 创建一个单独的版本。
  • 试试这个? rextester.com/edit/DEVHNG47817 Seq 并不重要,由于 TOP 1 如何获取默认顺序,它可能有所不同,我在其中放置了一个 order by 子句,只是为了与您的示例相匹配。它所做的只是正确排序结果

标签: sql-server recursion common-table-expression


【解决方案1】:

这将返回您想要的结果,但对于 @nodeId int = 5; 的情况,它不会返回任何内容,因为没有与该节点关联的路径。

我从cte.[From] 得到Name's,然后从last id cte.[To]

添加另一个Name

我添加了一个来源,以确保顺序是您在输出中显示的顺序。否则因为最后两个Name's 属于同一行可能会倒序。

SQL DEMO

;WITH cte AS (

    -- ANCHOR
    -- Find one path involving Node
    SELECT top 1 p.*, 0 as [Seq] FROM dbo.Path p WHERE [From] = @nodeId or [To] = @nodeId order by [From] desc

    union all

    -- go left
    select leftPath.*, cte.[Seq] - 1 as [Seq]
    from [Path] leftPath
    join cte on cte.[From] = leftPath.[To] and cte.[Seq] <= 0

    union all

    -- go right
    select rightPath.*, cte.[Seq] + 1 as [Seq]
    from [Path] rightPath
    join cte on cte.[To] = rightPath.[From] and cte.[Seq] >= 0
)

SELECT 'A' as [Source], cte.Id, cte.Seq, cte.[From], cte.[To], Node.[Name]
FROM cte 
JOIN Node
  ON cte.[From] = Node.[Id]
UNION  
SELECT 'B' as [Source], cte.Id, cte.Seq, cte.[From], cte.[To], Node.[Name]
FROM cte 
JOIN Node
  ON cte.[To] = Node.[Id]  
WHERE cte.id = (SELECT Max(Id) FROM cte)  

order by [Source], [Seq]

输出

@nodeId int = 1 to 4 same result:

@nodeId int = 6 to 7 same result:

如果您将前面的查询也放入 CTE 并调用 step2,您可以使用如下格式:

SELECT ROW_NUMBER() OVER (ORDER BY [Source], [Seq]) as [Id],
       Name
FROM step2

最终输出

【讨论】:

    【解决方案2】:

    我的意思是这样的:

    DECLARE @nodeId int = 2;   
    
    select 
    *
    from Node t1
    inner join Path t2
    on t1.Id = t2.[From] or t1.Id = t2.[To]
    where t1.Id = @nodeId
    order by t1.Id
    

    我知道,上述查询不能解决您的问题,但是,使用此查询,您得到的结果与使用 cte 相同,而且更简单。从此查询中,如果查询尚未执行,您期望的结果是什么。

    但我知道这不是你想要的。为此,我需要一个您想要的结果的完整示例,我阅读的最后一段是表节点。 ¿?

    【讨论】:

    • 你说得对,一个更复杂的样本应该否则select * From Node order by id也返回期望的输出。
    • 更新问题。添加了 3 个新节点和 1 个新路径。所以现在有 2 条路径 - W-X-Y-Z 和 B-C。运行我的原始查询宽度 nodeid 2 产生与以前相同的结果 - 涉及整个路径 nodeid 2(名为 X)。我的查询返回图形的边缘,我希望它返回顶点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    相关资源
    最近更新 更多