为何MS SQL中SELECT语句在递归CTE与普通查询中行为不同?
为什么MS SQL中递归CTE的SELECT行为与普通查询不同?
核心原因是递归CTE的执行逻辑和普通查询/UNION ALL查询完全不同,它不是对完整结果集的集合遍历,而是基于迭代的递归生成过程:
1. 普通查询与UNION ALL查询的逻辑
这两种场景都是标准的集合操作:
- 普通查询会遍历目标表的所有行,执行变量拼接时会逐行更新
@txt,最终得到所有值的拼接结果。 - UNION ALL查询是把两个结果集合并成一个完整的集合,SELECT同样会遍历这个合并后的所有行,行为和普通查询一致。
对应代码示例:
-- 普通查询场景 create table #alpha3 (names varchar(max)) insert into #alpha3 values ('a'), ('aa') declare @txt varchar(max) select top 10 @txt = concat(@txt, names, ' ') from #alpha3 select @txt;
-- UNION ALL查询场景 create table #beta (names varchar(max)) insert into #beta values ('a'), ('aa') select * from ( select * from #beta union all select * from #beta) as aa
2. 递归CTE的执行逻辑
递归CTE由锚点成员和递归成员两部分组成,执行流程是迭代式的:
- 先执行锚点成员,生成初始结果行(
names='aa', num=0); - 接着执行递归成员,仅基于上一轮递归输出的结果集(而不是整个CTE的所有行)来生成新行——也就是用前一次的
names拼接'a',num加1; - 重复步骤2,直到递归成员的WHERE条件(
num<10)不满足时停止。
整个过程是逐步生成新行,而不是遍历已有的所有行,所以你看到的是“仅取上一行的最后值递归”的行为。
对应递归CTE代码:
with cte as ( select cast('aa' as varchar(max)) as names, 0 as num union all select cast (names + 'a' as varchar(max)), num+1 from cte where num<10 ) select * from cte
内容的提问来源于stack exchange,提问作者Ali
相关产品推荐
相关产品推荐

