递归CTE返回Path中所有连接的节点

时间:2017-08-01 20:21:56

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

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

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查询,它给出了任何Node 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

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

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

2 个答案:

答案 0 :(得分:0)

我所说的是:

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相同,而且更简单。从此查询中,如果查询尚未执行,那么您期望的结果是什么。

但我明白这不是你想要的。为此,我需要一个你想要的结果的complet示例,我读到的最后一个段是表节点。 ¿?

答案 1 :(得分:0)

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

请参阅我从Name's获取cte.[From],然后从 last ID {{1}添加另一个Name }

我添加了一个来源,以确保订单是您在输出中显示的顺序。否则因为最后两个属于同一行的cte.[To]可能具有反转顺序。

<强> SQL DEMO

Name's

<强>输出

;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]

enter image description here

@nodeId int = 1 to 4 same result:

enter image description here

如果您将上一个查询也放在CTE中并调用@nodeId int = 6 to 7 same result: ,您可以按照以下格式设置:

step2

最终输出

enter image description here