【问题标题】:How to get Ancient Father of each son Recursive SQL如何获得每个儿子递归 SQL 的古代父亲
【发布时间】:2019-05-24 11:25:06
【问题描述】:

我有这张原始表格:

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

如果你能提供帮助,我会很高兴, 提前致谢

【问题讨论】:

  • 为什么E的新节点会是AAE父级,而不是相反。请解释你的逻辑。
  • 我想得到每个原始节点的古代父亲。A是E的古代父亲,就像A是C和D的古代父亲一样。

标签: sql sql-server recursion


【解决方案1】:

这是使用递归 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;

Demo

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

SELECT *
FROM cte
ORDER BY ID;

希望很清楚这个技巧是如何工作的。我们递归地构建一条路径,从所有原始祖先开始。当递归 CTE 完成时,我们有一个表,其中每个节点都有一条完整的路径,一直到原始祖先。然后,我们只需要取序列中的第一项来找到那个祖先。

【讨论】:

  • 感谢蒂姆,实际上它是一个完整的答案,有很大帮助。唯一的问题是名称字段不完全相同。非常感谢。
【解决方案2】:

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

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

结果:

【讨论】:

    【解决方案3】:

    我知道它并不完美,但这个可以正常工作并提供您想要的结果:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-19
      相关资源
      最近更新 更多