Поиск родителя верхнего уровня в SQL - программирование
Подтвердить что ты не робот

Поиск родителя верхнего уровня в SQL

У меня есть две таблицы:

Таблица 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 являются внешними ключами столбца Id таблицы Person

Мне нужно написать SQL, который может принести мне верхний уровень родительского i-e Root. Может кто-нибудь предложить любой SQL, который может помочь мне выполнить этот

4b9b3361

Ответ 1

Вы можете использовать рекурсивный 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 DEMO

EDIT - для обновленного запроса родителей верхнего уровня для всех детей:

;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 DEMO

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 DEMo

Ответ 2

Я использовал этот шаблон для связывания элементов в иерархии с корнем элемента node. Существенно рекурсивные иерархии, поддерживающие значения корня node, в качестве дополнительных столбцов, добавленных к каждой строке. Надеюсь, это поможет.

    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

Ответ 3

Что-то вроде этого будет работать для примера выше:

SELECT ParentId FROM RelationHierarchy 
WHERE ParentId NOT IN (SELECT CHildId FROM RelationHierarchy)

Ответ 4

Чтобы найти всех родителей верхнего уровня, используйте запрос типа:

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 здесь.

Ответ 5

Попробуйте это.

Рекурсивный CTE найдет человека и поднимет иерархию, пока не найдет родителя.

-- 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)

Ответ 6

Единственный способ сделать это в "стандартном" SQL - это принять максимальную глубину для дерева, а затем сделать объединения для каждого уровня. Следующее получает идентификатор верхнего уровня:

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);

Ответ 7

Дайте этому ходу:

    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
    )

Недостаточно просто узнать, существует ли дочерний идентификатор, как в вашем примере E, присутствует в таблице person, но не в таблице отношений иерархии.