具有多个父项的T-SQL递归CTE

时间:2013-10-24 19:07:30

标签: sql recursion

我发现的所有递归CTE示例都使用了基本相同的场景。它们都在组织结构图中向上或向下移动。我的CTE非常适合一对一的父母/子女关系,比如一个员工有一个经理,但我有一张桌子,孩子可以有多个父母。我想要的是一个来自一个孩子的父母身份证明的清单。

如果您对细节感兴趣,我将通过MS Project文件中特定任务的前身进行递归。我想从一个特定的里程碑开始,一直追溯到文件的顶部,以便找到可能影响里程碑的任何前辈。您可能知道,任务可以有多个前任。

我的关系看起来像这样:

tblTask​​s

child    parent
3        1
16       1
25       1
25       3
25       16
26       1
26       3
27       25
27       26

这是我的CTE:

;WITH ProjectTrace(Task)
AS
(
    -- Anchor member definition (This is task 27)
    SELECT t.parent AS Task
    FROM #tblTasks t
    WHERE t.child = 27

    UNION ALL

    -- Recursive member definition (This is everything that hooks into 27 via predecessors)
    SELECT t.parent AS Task
    FROM #tblTasks t
    INNER JOIN ProjectTrace trace
        ON t.child = trace.Task
)
SELECT * FROM ProjectTrace ORDER BY Task

我想为查询提供任务#27,并在我的结果集中只获得1,3,16,25,26 。但是,由于递归的工作方式,我的结果集如下所示:

Task
1
1
1
1
1
3
3
16
25
26

如果你看一下这些关系,我想这是有道理的。我总是可以将选择结束更改为选择不同,但是当我深入了解项目时,比如任务编号500,它会返回数百万条记录。

我可能做错了什么?

2 个答案:

答案 0 :(得分:0)

我认为distinct是做到这一点的好方法。您还可以检查迭代插入解决方案:

declare @Temp table(Task int primary key)

insert into @Temp
select distinct parent from Table1 where child = 27

while 1 = 1
begin
    insert into @Temp
    select distinct T.parent
    from Table1 as T
    where
        exists (select * from @Temp as TT where TT.Task = T.child) and
        not exists (select * from @Temp as TT where TT.Task = T.parent)

    if @@rowcount = 0 break
end

select * from @Temp

不知道它是否会更快,请自行检查。

答案 1 :(得分:0)

我认为没有比DISTINCT更好的表现选项。

您不能使用LEFT JOIN来限制trace.Task IS NULL的位置,您不能GROUP BY。我认为DISTINCT是要走的路。