sql递归的执行步骤

文章地址:https://www.cnblogs.com/MLGB/p/7921579.html

今天用到了sql的递归查询。递归查询是CTE语句with xx as(....)实现的。

假如表Category数据如下。

我们想查找机枪这个子分类极其层次关系(通过子节点,查询所有层级节点)。以下是查询语句

 

1
2
3
4
5
6
7
8
9
WITH tt AS (
 
SELECT CategoryId,Name,Parent,0 level  FROM dbo.Category WHERE CategoryId=15  --定位点成员
UNION ALL
SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN tt ON tt.Parent=c.CategoryId  --递归成员
 
)
 
SELECT FROM tt

 执行结果

CTE递归说明:

上面的脚本,我注明了定位点成员,和递归成员。

定位点成员形成了查询的基本结果集。其实就是你用于查询的"引子"数据。在递归cte开始执行时,第一次执行,会先执行定位成员,并得到寄出结果集。

递归成员是每次递归执行的语句。个人意淫,每次递归时,是把上一次递归查询得出的结果集传递给递归语句,并继续执行递归

在递归时,如果上一次查询没有返回结果集,则终止递归。(这点让我纠结很久,因为没有显示的著名何时结束递归~)

意淫的执行步骤--------------------

首先执行定位成员:

1
SELECT CategoryId,Name,Parent,0 level  FROM dbo.Category WHERE CategoryId=46

并得到了结果集:

 

然后执行第一次递归:

1
SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN tt ON tt.Parent=c.CategoryId

执行递归时,join了 cte 语句本身,就是tt,join里的语句跟with as里的语句一样。其实语句可以意淫成酱紫

1
2
3
4
5
6
7
SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN
(
            --SELECT CategoryId,Name,Parent,0 level  FROM dbo.Category WHERE CategoryId=15  华丽的忽略掉了
            --UNION ALL  华丽的忽略掉了
            SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN tt ON tt.Parent=c.CategoryId --我们继续递归~
     
)tt ON tt.Parent=c.CategoryId

这次递归可以拆分成两个操作,查询结果和继续递归。

1.因为是第一次递归,所以将执行定位成员得出的结果集带入,进行查询。其实执行的是酱紫

1
2
3
4
5
SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN
(
        SELECT CategoryId,Name,Parent,0 level  FROM dbo.Category WHERE CategoryId=46
     
)tt ON tt.Parent=c.CategoryId

发现有结果:

则继续进行递归:

递归时语句还是酱紫:

1
2
3
4
5
6
7
SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN
(
            --SELECT CategoryId,Name,Parent,0 level  FROM dbo.Category WHERE CategoryId=15  华丽的忽略掉了
            --UNION ALL  华丽的忽略掉了
            SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN tt ON tt.Parent=c.CategoryId --我们继续递归
     
)tt ON tt.Parent=c.CategoryId

这次递归查询,所用的结果集是上次递归查询得出的(不再是定位成员的结果集了)。就是用的下面这个结果集

执行语句是酱紫的

1
2
3
4
5
6
7
8
SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN
(
        SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN
    (
            SELECT CategoryId,Name,Parent,0 level  FROM dbo.Category WHERE CategoryId=46
     
    )tt ON tt.Parent=c.CategoryId
)tt ON tt.Parent=c.CategoryId

第一层join的数据是上次递归查询的语句。

发现有结果:

ok继续递归吧

继续上面的步骤,带入上次查询的结果集进行查询。

执行语句其实是酱紫:

1
2
3
4
5
6
7
8
9
10
11
SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN
(
        SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN
        (
                    SELECT c.CategoryId,c.Name,c.Parent,tt.level+1 level FROM Category c JOIN
                (
                            SELECT CategoryId,Name,Parent,0 level  FROM dbo.Category WHERE CategoryId=46
     
                )tt ON tt.Parent=c.CategoryId
        )tt ON tt.Parent=c.CategoryId
)tt ON tt.Parent=c.CategoryId

得出的结果集:

啥也木有,终止递归。

然后用union all 把每次递归的结果集合起来。得出了最终的查询结果。

这个步骤是意淫的,我还设想了其他的方式。但在终止递归上都存在问题,所以最终把文章写成酱紫。

posted @ 2019-08-09 00:56  mimo0  阅读(588)  评论(0编辑  收藏  举报