今天用到了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 把每次递归的结果集合起来。得出了最终的查询结果。
这个步骤是意淫的,我还设想了其他的方式。但在终止递归上都存在问题,所以最终把文章写成酱紫。