2010-06-25 14 views
24

Dato una tabella con una colonna di tipo hierarchyid, come si scrive una query per restituire tutte le righe che sono antenati di un nodo specifico?Come si ottengono tutti gli antenati di un nodo utilizzando la gerarchia di SQL Server 2008?

C'è una funzione IsDescendantOf(), che è perfetto per ottenere i bambini, ma non c'è corrispondente IsAncestorOf() funzione per restituire gli antenati (e l'assenza di una funzione GetAncestors() sembra piuttosto una svista.)

+9

non è 'child.IsDescendantOf (genitore)' lo stesso 'parent.IsAncestorOf (bambino)'? – Gabe

risposta

24

Il più comunemente usato approccio sarebbe ricorsiva un'espressione di tabella comune (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 

(adattato da un Simon Ince blog post)

Simon Ince anche propone un secondo approccio in cui si inverte appena fondamentalmente la condizione - invece di rilevare quelle voci persona che sono un antenato della persona bersaglio, si gira l'assegno intorno:

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 

Questo selezionerà tutte le righe dalla tabella, in cui la persona target a cui sei interessato è un discendente di qualsiasi livello nella gerarchia. Quindi questo troverà gli antenati immediati e non immediati della persona bersaglio fino alla radice.

+5

In quel blogpost, questa soluzione CTE non è seguita da una più semplice ("Funziona bene, ma è il modo migliore per ottenerlo? No, proviamo ancora!")? – AakashM

+0

@AakashM: sì, c'è una seconda opzione, anzi - non una che probabilmente userò, ma funzionerà anche dal suo aspetto. –

+0

So che è molto vecchio, ma lo scrivo per i futuri lettori: Il metodo di "Simon Ince blog post" è quasi 100 volte più lento del metodo "CTE" quando il piano di esecuzione non esiste. – Achilles

12

Ecco una risposta rotolato in un unico selezionare:

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

Il primo predicato della clausola where è ridondante poiché un genitore è sempre un discendente di se stesso. 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

Anche se si dispone di un indice sul hierarchyID, dovrà valutare IsDesendentOf per ogni riga, no? Penso di avere un modo migliore (vedi la mia risposta) –

0

ho scritto una funzione con valori di tabella definita dall'utente che si espande un valore hierarchyid nei suoi antenati costituenti. L'output può quindi essere ricollocato sulla colonna hierarchyid per ottenere specificatamente gli antenati.

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 

Per andare sul suo utilizzo:

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

per favore aiutami a risolvere questo problema. http://stackoverflow.com/questions/44016261/how-do-you-get-recursivelevel-using-sql-server-2012-hierarchyid – ManojKanth

Problemi correlati