2009-11-18 62 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