2010-06-25 17 views
24

Cho một bảng có cột loại hierarchyid, làm cách nào để bạn viết truy vấn trả về tất cả các hàng là tổ tiên của một nút cụ thể?Làm thế nào để bạn có được tất cả tổ tiên của một nút bằng cách sử dụng phân cấp SQL Server 2008?

Có một IsDescendantOf() chức năng, mà là hoàn hảo để nhận được con, nhưng không có IsAncestorOf() chức năng tương ứng với trở lại tổ tiên (và sự vắng mặt của một hàm GetAncestors() dường như khá một giám sát.)

+9

Không phải là 'child.IsDescendantOf (mẹ) 'giống như' parent.IsAncestorOf (con) '? – Gabe

Trả lời

24

Việc phổ biến nhất được sử dụng cách tiếp cận sẽ là một bảng thông thường Biểu hiện đệ quy (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 

(chuyển thể từ một Simon Ince blog post)

Simon Ince cũng đề xuất một cách tiếp cận thứ hai nơi ông chỉ về cơ bản đảo ngược tình trạng này - thay vì phát hiện những mục người đó là tổ tiên của người mục tiêu, anh quay kiểm tra xung quanh:

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 

này sẽ chọn tất cả các hàng từ bảng của bạn, nơi mà người mà bạn quan tâm đến là một hậu duệ của - bất kỳ cấp độ nào trong hệ thống phân cấp. Vì vậy, điều này sẽ tìm thấy tổ tiên ngay lập tức và không ngay lập tức của mục tiêu tất cả các con đường lên đến gốc.

+5

Trong blogpost đó, không phải giải pháp CTE này, sau đó tiếp theo là giải pháp đơn giản hơn ("Điều này hoạt động tốt, nhưng đó có phải là cách tối ưu để đạt được nó không? Không. Hãy thử lại!")? – AakashM

+0

@AakashM: vâng, có một lựa chọn thứ hai, thực sự - không phải là một cái mà tôi có thể sẽ sử dụng, nhưng nó cũng sẽ hoạt động, từ vẻ ngoài của nó. –

+0

Tôi biết điều này rất cũ, nhưng tôi viết điều này cho người đọc trong tương lai: Phương pháp từ "bài đăng trên blog Simon Ince" chậm hơn gần 100 lần so với phương pháp "CTE" khi kế hoạch thực hiện không tồn tại. – Achilles

12

Dưới đây là một câu trả lời cuộn lại thành một đơn chọn:

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

Vị từ đầu tiên của mệnh đề where là thừa vì bố mẹ luôn là hậu duệ của chính nó. 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

Thậm chí nếu bạn có chỉ mục trên hệ thống phân cấp, nó sẽ phải đánh giá IsDesendentOf cho mỗi hàng, không? Tôi nghĩ rằng tôi có một cách tốt hơn (xem câu trả lời của tôi) –

0

tôi đã viết một hàm bảng giá trị người dùng định nghĩa mở rộng một giá trị HierarchyId vào tổ tiên cấu thành của nó. Sau đó, đầu ra có thể được nối lại trên cột phân cấp để có được những tổ tiên đó một cách cụ thể.

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 

Để đi về việc sử dụng nó:

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

hãy giúp tôi giải quyết vấn đề này. http://stackoverflow.com/questions/44016261/how-do-you-get-recursivelevel-using-sql-server-2012-hierarchyid – ManojKanth

Các vấn đề liên quan