将递归应用于SQL Server查询

时间:2014-04-26 04:57:07

标签: sql sql-server sql-server-2008 recursion recursive-query

SQL Server中的表具有多代的父引用的子映射。

我必须达到结果:

Level No || Child Count

所以,我有以下查询:

SELECT 
    'L1' LEVEL, COUNT(ChildRef) CHILD 
FROM 
    [dbo].[TOMatriX] 
WHERE 
    ParentRef = 1

UNION ALL

SELECT 
   'L2' LEVEL, COUNT(ChildRef) CHILD 
FROM 
   [dbo].[TOMatriX]
WHERE 
   ParentRef IN (SELECT ChildRef FROM [dbo].[TOMatriX] WHERE ParentRef = 1)

UNION ALL

SELECT 
   'L3' LEVEL, COUNT(ChildRef) CHILD 
FROM 
   [dbo].[TOMatriX]
WHERE 
   ParentRef IN (SELECT ChildRef FROM [dbo].[TOMatriX]
                 WHERE ParentRef IN (SELECT ChildRef FROM [dbo].[TOMatriX] 
                                     WHERE ParentRef = 1)
                )

UNION ALL

SELECT 
   'L4' LEVEL, COUNT(ChildRef) CHILD 
FROM 
   [dbo].[TOMatriX]
WHERE 
   ParentRef IN (SELECT ChildRef FROM [dbo].[TOMatriX]
                 WHERE ParentRef IN (SELECT ChildRef FROM [dbo].[TOMatriX]
                                     WHERE ParentRef IN (SELECT ChildRef 
                                                         FROM [dbo].[TOMatriX] 
                                                         WHERE ParentRef = 1)
                                    )
                )

如何使此查询动态化?没有硬编码,因为水平也可以超过4。

如何使用Common Table Expressions或其中的任何递归?

1 个答案:

答案 0 :(得分:1)

说我们有下表:

create table #t1 (childref int, parentref int)
go
insert #t1 values
(1, null),
(2, 1),
(3, 1),
(4, 2),
(5, 2),
(6, 4),
(7, 4),
(8, 5),
(9, 6)
go

我们可以使用递归CTE来遍历层次结构并计算每个级别的项目:

;with x as (
    select childref, 0 as lvl
    from #t1
    where parentref is null

    union all

    select #t1.childref, x.lvl+1
    from #t1
    inner join x on #t1.parentref = x.childref
)
select lvl, count(*)
from x
group by lvl

这是你想要的结果。让我们看看它是如何运作的。

递归CTE有两个部分(简单来说):锚点和resursive部分。主播:

    select childref, 0 as lvl
    from #t1
    where parentref is null

执行一次,结果暂时存储。然后执行递归成员

    select #t1.childref, x.lvl+1
    from #t1
    inner join x on #t1.parentref = x.childref

查看内部联接?这里的'x'实际上是对锚成员的结果的引用。我们加入的列必须包含在选择列表中。

存储此步骤的结果,并且是下一步骤的基础。

再次执行递归成员,这次加入上一步的结果。依此类推,只要递归步骤返回任何行,或者未达到maxrecursion。

UNION ALL这里只是将这两者连接在一起的语法。

lvl被添加到选择中以表示当前级别,并在每一步增加。

如果您在CTE之后只是select * from x,则可以看到原始输出。我们所做的是用lvl对它进行分组并计算行数。