2010-06-25 15 views
24

Dlaczego w tabeli zawierającej kolumnę typu hierarchyid zwracane są wszystkie wiersze będące przodkami określonego węzła?Jak uzyskać wszystkich przodków węzła przy użyciu hierarchyid programu SQL Server 2008?

Jest IsDescendantOf() funkcja, która jest idealnym miejscem na dzieci, ale nie ma co odpowiada IsAncestorOf() funkcja zwraca przodków (i brak GetAncestors() funkcji wydaje się dość przeoczenie.)

+9

nie jest 'child.IsDescendantOf (rodzic)' taka sama jak 'parent.IsAncestorOf (dziecko)'? – Gabe

Odpowiedz

24

Najpowszechniej stosowanym podejście byłoby rekurencyjnego Expression tabelowym (CTE)

WITH Ancestors(Id, [Name], AncestorId) AS 
(
     SELECT 
      Id, [Name], Id.GetAncestor(1) 
     FROM 
      dbo.HierarchyTable 
     WHERE 
      Name = 'Joe Blow' -- or whatever you need to select that node 

     UNION ALL 

     SELECT 
      ht.Id, ht.[Name], ht.Id.GetAncestor(1) 
     FROM 
      dbo.HierarchyTable ht 
     INNER JOIN 
      Ancestors a ON ht.Id = a.AncestorId 
) 
SELECT *, Id.ToString() FROM Ancestors 

(adaptacja Simon Ince blog post)

Simon Ince również Proponuje drugie podejście, gdzie właśnie zasadzie odwraca stan - zamiast wykrywać te dane osobowe, które są przodek osoby docelowej, odwraca zaznaczenie wokół:

DECLARE @person hierarchyid 

SELECT @person = Id 
FROM dbo.HierachyTable 
WHERE [Name] = 'Joe Blow'; 

SELECT 
    Id, Id.ToString() AS [Path], 
    Id.GetLevel() AS [Level], 
    Id.GetAncestor(1), 
    Name 
FROM 
    dbo.HierarchyTable 
WHERE 
    @person.IsDescendantOf(Id) = 1 

Pozwoli to wybrać wszystkie wiersze z tabeli, gdzie docelową osobą, którą jesteś zainteresowany, jest potomek - dowolnego poziomu w hierarchii. Dzięki temu bezpośrednie i nieodległe przodki danej osoby docelowej znajdą się aż do rdzenia.

+5

W tym blogu nie jest to rozwiązanie CTE, a następnie prostsze ("Działa to dobrze, ale czy jest to optymalny sposób osiągnięcia tego? Nie. Spróbujemy jeszcze raz!")? – AakashM

+0

@AakashM: tak, jest druga opcja, w rzeczy samej - nie taka, której bym prawdopodobnie użył, ale też będzie działała z jej wyglądu. –

+0

Wiem, że to jest bardzo stare, ale piszę to dla przyszłych czytelników: Metoda z "Postu na blogu Simon Ince" jest prawie 100 razy wolniejsza niż metoda "CTE", gdy plan wykonania nie istnieje. – Achilles

12

Oto odpowiedź zwinięty w pojedynczy wybrać:

SELECT t1.Id.ToString() as Path, t1.Name 
    FROM (SELECT * FROM HierarchyTable 
     WHERE Name = 'Joe Blow') t2, 
    HierarchyTable t1 
    WHERE t2.Id.IsDescendantOf(t1.Id) = 1 
+0

Pierwszy predykat klauzuli where jest zbędny, ponieważ rodzic zawsze jest potomkiem samego siebie. http://msdn.microsoft.com/en-us/library/bb677203(v=sql.105).aspx – influent

2
Declare @hid hierarchyid=0x5D10 -- Child hierarchy id 

SELECT 
* 
FROM 
    dbo.TableName 
WHERE 
    @hid.IsDescendantOf(ParentHierarchyId) = 1 
+0

Nawet jeśli masz indeks na hierarchyID, będzie musiał ocenić IsDesendentOf dla każdego wiersza, nie? Myślę, że mam lepszy sposób (zobacz moją odpowiedź) –

0

Napisałem wycenione tabela funkcji zdefiniowanej przez użytkownika, który rozszerza wartość hierarchyid do swych przodków składowych. Dane wyjściowe można następnie połączyć z powrotem w kolumnie hierarchyid, aby uzyskać dokładnie tych przodków.

alter function dbo.GetAllAncestors(@h hierarchyid, @ReturnSelf bit) 
returns table 
as return 
select @h.GetAncestor(n.Number) as h 
from dbo.Numbers as n 
where n.Number <= @h.GetLevel() 
    or (@ReturnSelf = 1 and n.Number = 0) 

union all 

select @h 
where @ReturnSelf = 1 
go 

Aby przejść o używaniu go:

select child.ID, parent.ID 
from dbo.yourTable as child 
cross apply dbo.GetAllAncestors(child.hid, 1) as a 
join dbo.yourTable as parent 
    on parent.hid = a.h 
+0

pomóż mi rozwiązać ten problem. http://stackoverflow.com/questions/44016261/how-do-you-get-recursivelevel-using-sql-server-2012-hierarchyid – ManojKanth