Finding Top level parent of each row of a table [SQL Server 2008]

前端 未结 3 760
醉话见心
醉话见心 2021-01-23 12:24

I have following two tables

Table Person

Id   Name
   1    A
   2    B
   3    C
   4    D
   5    E

Table RelationHierarchy



        
相关标签:
3条回答
  • 2021-01-23 12:33

    You could try to use a loop. As you will get many levels of recursion with your approach:

    declare @child int = 0
    declare @parent int = 1 --child to search
    while @child <> @parent 
    BEGIN
    set @child = @parent
    select  @parent = Parentid from @parentchild  where ChildID = @child
    END
    select @parent
    
    0 讨论(0)
  • 2021-01-23 12:52

    I have also updated the answer in the original question, but never-mind, here is a copy also:

    ;WITH RCTE AS
    (
        SELECT  ParentId, ChildId, 1 AS Lvl FROM RelationHierarchy 
    
        UNION ALL
    
        SELECT rh.ParentId, rc.ChildId, Lvl+1 AS Lvl 
        FROM dbo.RelationHierarchy rh
        INNER JOIN RCTE rc ON rh.ChildId = rc.ParentId
    )
    ,CTE_RN AS 
    (
        SELECT *, ROW_NUMBER() OVER (PARTITION BY r.ChildID ORDER BY r.Lvl DESC) RN
        FROM RCTE r
    
    )
    SELECT pc.Id AS ChildID, pc.Name AS ChildName, r.ParentId, pp.Name AS ParentName
    FROM dbo.Person pc 
    LEFT JOIN CTE_RN r ON pc.id = r.CHildId AND  RN =1
    LEFT JOIN dbo.Person pp ON pp.id = r.ParentId
    

    SQLFiddle DEMO

    Note that the slight difference is in recursive part of CTE. ChildID is now rewritten each time from the anchor part. Also addition is ROW_NUMBER() function (and new CTE) to get the top level for each child at the end.

    EDIT - Version2

    After finding a performance issues with first query, here is an improved version. Going from top-to-bottom, instead of other way around - eliminating creating of extra rows in CTE, should be much faster on high number of recursions:

    ;WITH RCTE AS
    (
        SELECT  ParentId, CHildId, 1 AS Lvl FROM RelationHierarchy r1
        WHERE NOT EXISTS (SELECT * FROM RelationHierarchy r2 WHERE r2.CHildId = r1.ParentId)
    
        UNION ALL
    
        SELECT rc.ParentId, rh.CHildId, Lvl+1 AS Lvl 
        FROM dbo.RelationHierarchy rh
        INNER JOIN RCTE rc ON rc.CHildId = rh.ParentId
    )
    SELECT pc.Id AS ChildID, pc.Name AS ChildName, r.ParentId, pp.Name AS ParentName
    FROM dbo.Person pc 
    LEFT JOIN RCTE r ON pc.id = r.CHildId
    LEFT JOIN dbo.Person pp ON pp.id = r.ParentId 
    

    SQLFiddle DEMO

    0 讨论(0)
  • 2021-01-23 12:55

    Another way to do this in a loop if you wanted to work with sets is:

    SELECT *
        INTO #parentchild
    from RelationHierarchy 
    
    
    WHILE EXISTS
    (select 1 from  #parentchild A inner join #parentchild B on A.ChildID = B.ParentID Where A.ParentID <> B.ParentID )
    BEGIN
    update B set B.ParentID = A.ParentID
    from #parentchild A inner join #parentchild B on A.ChildID = B.ParentID
    END
    
    select * from #parentchild
    
    0 讨论(0)
提交回复
热议问题