24

我有两个表如下

桌子Person

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

桌子RelationHierarchy

ParentId   ChildId
   2         1
   3         2
   4         3

这将形成一个树状结构

          D
          |
          C
          |
          B
          |
          A

ParentId 和 ChildId 是 Person 表的 Id 列的外键

我需要编写可以获取我顶级父级即根的 SQL。谁能建议任何可以帮助我完成此任务的 SQL

4

9 回答 9

34

您可以使用递归 CTE来实现:

DECLARE @childID INT 
SET @childID  = 1 --chield to search

;WITH RCTE AS
(
    SELECT *, 1 AS Lvl FROM RelationHierarchy 
    WHERE ChildID = @childID

    UNION ALL

    SELECT rh.*, Lvl+1 AS Lvl FROM dbo.RelationHierarchy rh
    INNER JOIN RCTE rc ON rh.CHildId = rc.ParentId
)
SELECT TOP 1 id, Name
FROM RCTE r
inner JOIN dbo.Person p ON p.id = r.ParentId
ORDER BY lvl DESC

SQLFiddle 演示

编辑- 对于所有孩子的顶级父母的更新请求:

;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 r.ChildId, pc.Name AS ChildName, r.ParentId, pp.Name AS ParentName
FROM CTE_RN r
INNER JOIN dbo.Person pp ON pp.id = r.ParentId
INNER JOIN dbo.Person pc ON pc.id = r.ChildId
WHERE RN =1

SQLFiddle 演示

EDIT2 - 让所有人在最后改变一点:

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 演示

于 2013-07-16T13:11:40.790 回答
10

我已使用此模式将层次结构中的项目与项目的根节点相关联。

本质上递归维护根节点的值作为附加到每一行的附加列的层次结构。希望这可以帮助。

with allRows as (
    select ItemId, ItemName, ItemId [RootId],ItemName [RootName] 
    from parentChildTable
    where ParentItemId is null
    union all
    select a1.ItemId,a1.ItemName,a2.[RootId],a2.[RootName]
    from parentChildTable a1
    join allRows a2 on a2.ItemId = a1.ParentItemId
)   

select * from allRows
于 2013-10-31T12:12:27.080 回答
6

要查找所有顶级父级,请使用如下查询:

select p.Name
from Person p
where not exists
(select null
 from RelationHierarchy r
 where r.ChildId = p.Id)

SQLFiddle在这里

要查找特定子级的顶级父级,请使用:

with cte as
(select t.ParentId TopParent, t.ChildId 
 from RelationHierarchy t
 left join RelationHierarchy p on p.ChildId = t.ParentId
 where p.ChildId is null
 union all
 select t.TopParent TopParent, c.ChildId 
 from cte t
 join RelationHierarchy c on t.ChildId = c.ParentId)
select p.name
from cte h
join Person p on h.TopParent = p.Id
where h.ChildId=3 /*or whichever child is required*/

SQLFiddle在这里

于 2013-07-16T13:07:50.620 回答
3

Try this.

The recursive CTE will find the person and walk up the hierarchy until it finds no parent.

-- This CTE will find the ancestors along with a measure of how far up
-- the hierarchy each ancestor is from the selected person.
with ancestor as (
  select ParentId as AncestorId, 0 as distance
  from RelationHierarchy
  where CHildId = ?

  union all

  select h.ParentId, a.distance + 1
  from ancestor a inner join RelationHierarchy rh on a.AncestorId = rh.ChildId
)
select AncestorId
from ancestor
where distance = (select max(distance) from ancestor)
于 2013-07-16T13:15:59.163 回答
2

像这样的东西适用于上面的例子:

SELECT ParentId FROM RelationHierarchy 
WHERE ParentId NOT IN (SELECT CHildId FROM RelationHierarchy)
于 2013-07-16T12:54:11.857 回答
1

您可以在“标准”SQL 中执行此操作的唯一方法是假设树的最大深度,然后对每个级别进行连接。以下获取顶级 id:

select rh1.ChildId,
       coalesce(rh4.parentid, rh3.parentid, rh2.parentid, rh1.parentid) as topLevel
from RelationshipHierarchy rh1 left outer join
     RelationshipHierarchy rh2
     on rh1.parentId = rh2.childId left outer join
     RelationshipHierarchy rh3
     on rh2.parentId = rh3.childId left outer join
     RelationshipHierarchy rh4
     on rh3.parentId = rh4.childId;

如果你想要这个名字,你可以加入它:

select rh1.ChildId,
       coalesce(rh4.parentid, rh3.parentid, rh2.parentid, rh1.parentid) as topLevel,
       p.name
from RelationshipHierarchy rh1 left outer join
     RelationshipHierarchy rh2
     on rh1.parentId = rh2.childId left outer join
     RelationshipHierarchy rh3
     on rh2.parentId = rh3.childId left outer join
     RelationshipHierarchy rh4
     on rh3.parentId = rh4.childId left outer join
     Person p
     on p.id = coalesce(rh4.parentid, rh3.parentid, rh2.parentid, rh1.parentid);
于 2013-07-16T13:10:27.453 回答
1

使用路径获取所有顶级父母

路径格式:rootId/.../parentId/nodeId/

select t1.path from nodes t1 inner join nodes t2
on t1.path like t2.path+'%' 
group by t1.path 
having len(t1.path)-len(replace(t1.path, '/', ''))
=min(len(t2.path)-len(replace(t2.path, '/', '')))
于 2019-03-04T11:20:12.340 回答
0

Give this a go:

    select id,name
    from person p
    where not exists
    (
    select 1 
    from relationhierarchy r
    where r.childid= p.id
    )
    and exists
    (
    select 1 
    from relationhierarchy r
    where r.parentid= p.id
    )

It is not enough to just see if a child id exists as in your example E is present in the person table but not in the relationshiphierarchy table.

于 2013-07-16T13:00:09.410 回答
0
WITH CTE_MyTable AS (
    SELECT        Id, ParentId, NULL As RootParent, 1 As Lvl
    FROM            dbo.Ministry
    UNION ALL
    SELECT        a.id, b.ParentId, a.ParentId As RootParent, Lvl + 1
    FROM            CTE_MyTableAS a INNER JOIN
                                               dbo.MyTableAS b ON a.ParentId = b.Id
)
, CTE_Ministry_RN AS  (
    SELECT Id, RootParent, ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Lvl DESC) RN
    FROM CTE_Ministry
)

SELECT Id, ISNULL(RootParent, Id) As RootParent
FROM CTE_Ministry_RN
WHERE RN = 1
于 2020-11-18T07:05:11.770 回答