Finding Top Level Parent ID

前端 未结 3 1576
挽巷
挽巷 2021-01-28 10:10

In one table I have two columns like below

ID  ParentID
1   0x0
2   1
3   2
9   0x0
5   9
6   5
25  0x0
30  25

How to get top level parent ID

相关标签:
3条回答
  • 2021-01-28 10:25

    You can use a recursive CTE:

    WITH CTE AS
    (
        SELECT *, 1 [Level]
        FROM dbo.YourTable
        UNION ALL
        SELECT A.ID, B.[Parent ID], [Level] + 1
        FROM CTE A
        INNER JOIN dbo.YourTable B
            ON A.[Parent ID] = B.Id
    ), CTE2 AS
    (
        SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY [Level] DESC)
        FROM CTE
        WHERE [Parent ID] <> 0
    )
    SELECT ID, [Parent ID] [Top Level Parent Id]
    FROM CTE2
    WHERE RN = 1
    OPTION (MAXRECURSION 0)
    ;
    

    Here is a link with a demo.

    The results are:

    ╔════╦═════════════════════╗
    ║ ID ║ Top Level Parent Id ║
    ╠════╬═════════════════════╣
    ║  2 ║                   1 ║
    ║  3 ║                   1 ║
    ║  5 ║                   9 ║
    ║  6 ║                   9 ║
    ║ 30 ║                  25 ║
    ╚════╩═════════════════════╝
    
    0 讨论(0)
  • 2021-01-28 10:32

    Yet another shorter CTE :

    WITH cte AS(
          SELECT *, id AS topparent 
          FROM t 
          WHERE parentid IS NULL
      UNION ALL
          SELECT t.*, c.topparent 
          FROM t JOIN cte c ON c.id = t.parentid
          WHERE t.id <> t.parentid
    )
    SELECT * FROM cte
    

    Tested here : SQL Fiddle


    EDIT : This damn SQLfiddle is unresponsive again. Test DDL Code below

    CREATE  table t (id INT, parentid INT);
    INSERT INTO T VALUES (1  , NULL );
    INSERT INTO T VALUES (2  , 1    );           
    INSERT INTO T VALUES (3  , 2    );          
    INSERT INTO T VALUES (9  , NULL );          
    INSERT INTO T VALUES (5  , 9    );          
    INSERT INTO T VALUES (6  , 5    );          
    INSERT INTO T VALUES (25 , NULL );          
    INSERT INTO T VALUES (30 , 25   );
    
    0 讨论(0)
  • 2021-01-28 10:49

    If the top-level node always follows a single path to a single leaf node, then the desired result can be achieved by combining Breadcrumbs and recursive CTEs. The following example demonstrates the approach:

    CREATE TABLE #Items
    (
        ID              INT
        ,[ParentID]     INT
    )
    
    INSERT INTO #Items
    VALUES
    (1,     0x0),
    (2,     1),
    (3,     2),
    (9,     0x0),
    (5,     9),
    (6,     5),
    (25,    0x0),
    (30,    25)
    
    
    
    ;WITH CTE_Breadcrumbs
    AS
    (
        SELECT      ID
                    ,ParentID
                    ,CAST(ID AS NVARCHAR(1000)) AS [Path]
                    ,LEN(CAST(ID AS NVARCHAR(1000))) AS [PathLength]
                    ,ID AS [RootNodeID]
        FROM        #Items
        WHERE       ParentID = 0x0
        UNION ALL
        SELECT       Child.ID
                    ,Child.ParentID
                    ,CAST(Parent.[Path] + N'\' + CAST(Child.ID AS NVARCHAR(1000)) AS NVARCHAR(1000)) AS [Path]
                    ,LEN(CAST(Parent.[Path] + N'\' + CAST(Child.ID AS NVARCHAR(1000)) AS NVARCHAR(1000))) AS [PathLength]
                    ,CAST(
                        CASE 
                            WHEN CHARINDEX('\', [Path]) > 0 THEN LEFT([Path], CHARINDEX('\', [Path]) - 1)
                            ELSE Child.ParentID
                        END
                    AS INT)  AS [RootNodeID]
        FROM        #Items Child 
        INNER JOIN  CTE_Breadcrumbs Parent ON Child.ParentID = Parent.ID
    )
    ,
    CTE_FullPathID
    AS
    (
        SELECT      [RootNodeID]
                    ,MAX([PathLength]) AS [PathFullLength]
        FROM        CTE_Breadcrumbs
        GROUP BY    [RootNodeID]
    
    )
    SELECT      CTE_Breadcrumbs.RootNodeID
                ,CAST(
                    REVERSE(
                                CASE 
                                    WHEN CHARINDEX('\', REVERSE([Path])) > 0 THEN LEFT(REVERSE([Path]), CHARINDEX('\', REVERSE([Path])) - 1)
                                    ELSE NULL
                                END
                            )
                AS INT)  AS [LeafNodeID]
                ,CTE_Breadcrumbs.[Path]
    FROM        CTE_Breadcrumbs 
    INNER JOIN  CTE_FullPathID ON CTE_Breadcrumbs.[RootNodeID] = CTE_FullPathID.[RootNodeID] AND CTE_Breadcrumbs.PathLength = CTE_FullPathID.[PathFullLength]
    
    0 讨论(0)
提交回复
热议问题