SQL 递归查询,意淫CTE递归的执行步骤

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

假如表Category数据如下。

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

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开始执行时,第一次执行,会先执行定位成员,并得到寄出结果集。

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

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

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

首先执行定位成员:

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

并得到了结果集:

 

然后执行第一次递归:

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里的语句一样。其实语句可以意淫成酱紫

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.因为是第一次递归,所以将执行定位成员得出的结果集带入,进行查询。其实执行的是酱紫

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

发现有结果:

则继续进行递归:

递归时语句还是酱紫:

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

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

执行语句是酱紫的

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继续递归吧

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

执行语句其实是酱紫:

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 把每次递归的结果集合起来。得出了最终的查询结果。

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

参考文章:https://www.cnblogs.com/youngmin/p/6256478.html

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