2009-11-18 23 views
83

Cách đơn giản nhất để thực hiện tự tham gia đệ quy trong SQL Server là gì? Tôi có một bảng như sau:Cách đơn giản nhất để thực hiện tự tham gia đệ quy?

PersonID | Initials | ParentID 
1   CJ   NULL 
2   EB   1 
3   MB   1 
4   SW   2 
5   YT   NULL 
6   IS   5 

Và tôi muốn có thể nhận được các hồ sơ chỉ liên quan đến một hệ thống phân cấp bắt đầu với một người cụ thể. Vì vậy, Nếu tôi yêu cầu hệ thống cấp bậc của CJ bởi PersonID = 1 tôi sẽ nhận được:

PersonID | Initials | ParentID 
1   CJ   NULL 
2   EB   1 
3   MB   1 
4   SW   2 

Và đối với EB của tôi nhận được:

PersonID | Initials | ParentID 
2   EB   1 
4   SW   2 

tôi là một chút khó khăn về vấn đề này có thể không thể nghĩ như thế nào để làm điều đó ngoài một phản hồi có độ sâu cố định dựa trên một loạt các phép nối. Điều này sẽ xảy ra vì nó xảy ra bởi vì chúng tôi sẽ không có nhiều cấp độ nhưng tôi muốn làm điều đó đúng cách.

Cảm ơn! Chris.

+2

Bạn đang sử dụng phiên bản SQL Server nào? tức là Sql 2000, 2005, 2008? – chadhoc

+2

SO câu hỏi liên quan đến truy vấn đệ quy: http://stackoverflow.com/search?q=sql-server+recursive –

Trả lời

85
WITH q AS 
     (
     SELECT * 
     FROM mytable 
     WHERE ParentID IS NULL -- this condition defines the ultimate ancestors in your chain, change it as appropriate 
     UNION ALL 
     SELECT m.* 
     FROM mytable m 
     JOIN q 
     ON  m.parentID = q.PersonID 
     ) 
SELECT * 
FROM q 

Bằng cách thêm vào các điều kiện đặt hàng, bạn có thể giữ gìn trật tự cây:

WITH q AS 
     (
     SELECT m.*, CAST(ROW_NUMBER() OVER (ORDER BY m.PersonId) AS VARCHAR(MAX)) COLLATE Latin1_General_BIN AS bc 
     FROM mytable m 
     WHERE ParentID IS NULL 
     UNION ALL 
     SELECT m.*, q.bc + '.' + CAST(ROW_NUMBER() OVER (PARTITION BY m.ParentID ORDER BY m.PersonID) AS VARCHAR(MAX)) COLLATE Latin1_General_BIN 
     FROM mytable m 
     JOIN q 
     ON  m.parentID = q.PersonID 
     ) 
SELECT * 
FROM q 
ORDER BY 
     bc 

Bằng cách thay đổi ORDER BY điều kiện bạn có thể thay đổi thứ tự của các anh chị em.

+6

+1, ngoại trừ việc Chris sẽ cần 'PersonID = theIdYouAreLookingFor' thay vì' ParentID IS NULL'. – Heinzi

+0

Có thể nhận được kết quả tương tự bằng EntityFramework –

+1

@TheIndianProgrammmer: có thể nhận được kết quả tương tự mà không sử dụng EntityFramework hay không. – Quassnoi

18

Sử dụng CTEs bạn có thể làm theo cách này

DECLARE @Table TABLE(
     PersonID INT, 
     Initials VARCHAR(20), 
     ParentID INT 
) 

INSERT INTO @Table SELECT  1,'CJ',NULL 
INSERT INTO @Table SELECT  2,'EB',1 
INSERT INTO @Table SELECT  3,'MB',1 
INSERT INTO @Table SELECT  4,'SW',2 
INSERT INTO @Table SELECT  5,'YT',NULL 
INSERT INTO @Table SELECT  6,'IS',5 

DECLARE @PersonID INT 

SELECT @PersonID = 1 

;WITH Selects AS (
     SELECT * 
     FROM @Table 
     WHERE PersonID = @PersonID 
     UNION ALL 
     SELECT t.* 
     FROM @Table t INNER JOIN 
       Selects s ON t.ParentID = s.PersonID 
) 
SELECT * 
FROm Selects 
+0

Câu trả lời hoàn chỉnh tốt với WHERE \t PersonID = @PersonID quan trọng –

2

SQL 2005 hoặc mới hơn, CTEs là cách tiêu chuẩn để đi theo các ví dụ hiển thị.

SQL 2000, bạn có thể làm điều đó bằng UDFs -

CREATE FUNCTION udfPersonAndChildren 
(
    @PersonID int 
) 
RETURNS @t TABLE (personid int, initials nchar(10), parentid int null) 
AS 
begin 
    insert into @t 
    select * from people p  
    where [email protected] 

    while @@rowcount > 0 
    begin 
     insert into @t 
     select p.* 
     from people p 
     inner join @t o on p.parentid=o.personid 
     left join @t o2 on p.personid=o2.personid 
     where o2.personid is null 
    end 

    return 
end 

(mà sẽ làm việc vào năm 2005, nó chỉ là không phải là cách làm tiêu chuẩn của nó Điều đó nói rằng, nếu bạn thấy rằng cách dễ dàng hơn để làm việc. , chạy với nó)

Nếu bạn thực sự cần phải làm điều này trong SQL7, bạn có thể làm gần như trên trong một sproc nhưng không thể chọn từ nó - SQL7 không hỗ trợ UDF.

4

Truy vấn Quassnoi có thay đổi đối với bảng lớn. Phụ huynh có nhiều trẻ em hơn 10: Tạo thành str (5) row_number()

 
WITH q AS 
     (
     SELECT m.*, CAST(str(ROW_NUMBER() OVER (ORDER BY m.ordernum),5) AS VARCHAR(MAX)) COLLATE Latin1_General_BIN AS bc 
     FROM #t m 
     WHERE ParentID =0 
     UNION ALL 
     SELECT m.*, q.bc + '.' + str(ROW_NUMBER() OVER (PARTITION BY m.ParentID ORDER BY m.ordernum),5) COLLATE Latin1_General_BIN 
     FROM #t m 
     JOIN q 
     ON  m.parentID = q.DBID 
     ) 
SELECT * 
FROM q 
ORDER BY 
     bc 

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