如何获得每个儿子的远古父亲递归SQL

时间:2018-12-26 05:57:13

标签: sql sql-server recursion

我有这个原始表:

enter image description here

我需要通过递归SQL查询(可能带有'With'子句)获取此表,但不确定如何获取它:

enter image description here

如果能提供帮助,我们将非常高兴, 预先感谢

3 个答案:

答案 0 :(得分:3)

这是使用递归CTE的一种方法:

WITH cte AS (
    SELECT t1.ID, t1.Node, t1.Son, t1.Name, CONVERT(VARCHAR(MAX), t1.Node + ',') AS path
    FROM yourTable t1
    WHERE NOT EXISTS (SELECT 1 FROM yourTable t2 WHERE t1.Node = t2.Son)
    UNION ALL
    SELECT t1.ID, t1.Node, t1.Son, t1.Name, CONVERT(VARCHAR(MAX), path + ',' + t2.Son)
    FROM yourTable t1
    INNER JOIN cte t2
        ON t1.Node = t2.Son
)

SELECT
    ID,
    Node AS [Original Node],
    CASE WHEN CHARINDEX(',', path) = 0
         THEN path
         ELSE LEFT(path, CHARINDEX(',', path) - 1) END AS [New Node],
    Name
FROM cte
ORDER BY ID;

enter image description here

Demo

说明:要了解其工作原理,在CTE上查看以下直接查询的输出可能会有所帮助:

SELECT *
FROM cte
ORDER BY ID;

enter image description here

希望很清楚,技巧是如何起作用的。我们从所有原始祖先开始递归地构建一条道路。递归CTE完成后,我们将得到一个表,每个节点都有一个完整的路径,可以一直返回到原始祖先。然后,我们只需要采用系列中的第一个术语即可找到该祖先。

答案 1 :(得分:1)

设法回答自己,但与上面提到的标签不完全一样。

Drop table if exists ##Nodes
CREATE TABLE ##Nodes
(
 NodeID varchar(20)  NOT NULL,
 SonNodeID varchar(20) NULL,
 [Name] varchar(20) NULL
)

    enter code here

INSERT INTO ##Nodes (NodeID, SonNodeID, [Name]) VALUES ('A', 'C','First')
INSERT INTO ##Nodes (NodeID, SonNodeID, [Name]) VALUES ('B', NULL,'Second')
INSERT INTO ##Nodes (NodeID, SonNodeID, [Name]) VALUES ('C', 'D','Third')
INSERT INTO ##Nodes (NodeID, SonNodeID, [Name]) VALUES ('D', 'E','Fourth')
INSERT INTO ##Nodes (NodeID, SonNodeID, [Name]) VALUES ('E', NULL,'Fifth')
INSERT INTO ##Nodes (NodeID, SonNodeID, [Name]) VALUES ('F', 'I','Sixth')
INSERT INTO ##Nodes (NodeID, SonNodeID, [Name]) VALUES ('G', NULL,'Seventh')
INSERT INTO ##Nodes (NodeID, SonNodeID, [Name]) VALUES ('H', NULL,'Eighth')
INSERT INTO ##Nodes (NodeID, SonNodeID, [Name]) VALUES ('I', NULL,'Nineth')

WITH Nodes AS
(
   --initialization
   SELECT NodeID, SonNodeID, [Name], Father = NodeID , 1 AS GenerationsRemoved
   FROM ##Nodes

   UNION ALL

   ----recursive execution
   SELECT N.NodeID, N.SonNodeID, P.[Name], Father = P.Father, P.GenerationsRemoved + 1
   FROM Nodes AS P
      INNER JOIN ##Nodes AS N
      ON P.SonNodeID = N.NodeID
   WHERE P.GenerationsRemoved <= 100

)
SELECT a.NodeID, AncientFather = Father, a.[Name]
FROM    Nodes as a
        join 
            (
            Select  NodeID, MaxGen = max(GenerationsRemoved)
            from    Nodes 
            group by NodeID
            )as b   on a.NodeID = b.NodeID
                    and a.GenerationsRemoved = b.MaxGen
ORDER BY a.NodeID

结果:

enter image description here

答案 2 :(得分:0)

我知道它并不完美,但是这种方法效果很好,可以提供您想要的结果:

select t1.ID, t1.NODE as ORIGINAL_NODE,
CASE
   when t4.SON is not null then t4.NODE
   when t3.SON is not null then t3.NODE
   when t2.SON is not null then t2.NODE
   else t1.NODE
END as NEW_NODE,
CASE
   when t4.NAME is not null then t4.NAME
   when t3.NAME is not null then t3.NAME
   when t2.NAME is not null then t2.NAME
   else t1.NAME
END as NAME
from Table t1
left outer join Table t2
on t1.NODE = t2.SON
left outer join Table t3
on t2.NODE = t3.SON
left outer join Table t4
on t3.NODE = t4.SON
order by t1.ID