2009-11-18 58 views
83

在SQL Server中執行遞歸自連接最簡單的方法是什麼?我有這樣的表格:最簡單的方法來做遞歸自聯接?

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

而且我希望能夠獲得只與特定人員開始相關的記錄相關的記錄。所以,如果我要求CJ的層次由是PersonID = 1,我會得到:

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

而對於EB的我會得到:

PersonID | Initials | ParentID 
2   EB   1 
4   SW   2 

我有點憋屈這可能怎麼也想不到除了基於一系列連接的固定深度響應之外。這樣做會發生,因爲我們不會有很多關卡,但我想正確地做。

謝謝!克里斯。

+2

您正在使用哪個版本的SQL Server?即Sql 2000,2005,2008? – chadhoc 2009-11-18 16:31:34

+2

SO有關遞歸查詢的問題:http://stackoverflow.com/search?q=sql-server+recursive – 2009-11-18 16:34:54

回答

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 

通過添加排序條件,您可以保存樹順序:

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 

通過改變ORDER BY條件可以改變的兄弟姐妹的順序。

+6

+1,除了Chris需要'PersonID = theIdYouAreLookingFor'而不是'ParentID IS NULL'。 – Heinzi 2009-11-18 16:40:10

+0

是否有可能使用EntityFramework – 2012-11-23 11:14:27

+1

@TheIndianProgrammmer獲得相同的結果:可以在不使用EntityFramework的情況下獲得相同的結果。 – Quassnoi 2012-11-23 12:03:15

18

使用CTE你能做到這樣

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

重要的WHERE完整答案\t PersonID = @PersonID – 2013-09-19 15:15:04

2

SQL 2005或更高,熱膨脹係數是去按所示的例子的標準方式。

SQL 2000,您可以使用UDF的做到這一點 -

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 

(將在2005年的工作,它只是不這樣做呢?那說的標準的方式,如果你發現了更簡單的方式工作。 ,與它一起運行)

如果你真的需要在SQL7中做到這一點,你可以大致上述在一個存儲過程,但無法從中選擇 - SQL7不支持UDF。

4

對大表進行更改的Quassnoi查詢。父母有更多孩子然後10:格式化爲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