SQL递归CTE执行原理是分步迭代+自引用+隐式终止:先执行一次锚点查询获取初始数据,再反复执行递归成员关联上轮结果生成新行,直至无新数据时自动终止。

SQL 递归 CTE 的执行原理,核心是“分步迭代 + 自引用 + 隐式终止”。它不是一次性扫描全表,而是像搭积木一样,一层层展开数据,直到没有新行可生成为止。
锚点查询先跑一次,打下第一块基石
锚点(Anchor Member)是整个递归的起点,必须是非递归的独立查询,不能引用自身。它只执行一次,输出初始结果集——比如查出 CEO 的记录,或某个根节点的数据。这个结果会作为后续每一轮递归的“输入种子”。
- 必须放在 UNION ALL 左侧
- 字段数量、类型、顺序要和递归成员严格一致
- 常见写法:WHERE manager_id IS NULL(找顶层)、WHERE id = @root_id(指定起始点)
递归成员反复执行,逐层向下延伸
递归成员(Recursive Member)必须出现在 UNION ALL 右侧,且必须且只能引用一次 CTE 自身名称。数据库引擎会用上一轮输出的结果,去关联原始表,找出下一层子节点。
- 例如:用上一轮的 id 去匹配员工表的 manager_id,得到所有直接下属
- 每次迭代生成的新行,都会追加进最终结果集(UNION ALL 不去重)
- 不能出现 GROUP BY、聚合函数、TOP、OFFSET-FETCH 等限制性操作
终止不靠显式判断,而靠“无新数据”自动结束
递归不会无限运行。系统在每次执行完递归成员后检查:如果这一轮没返回任何新行,就立刻停止,合并所有轮次的结果返回给用户。这是一种隐式终止机制。
- 但实际中仍需主动防范无限循环:确保父子关系单向(如 parent_id ≠ id)、JOIN 条件能持续收敛
- 多数数据库提供深度控制手段:SQL Server 用 OPTION (MAXRECURSION n),MySQL 用 cte_max_recursion_depth 参数,PostgreSQL 虽无硬限,但栈溢出会报错
- 建议加 level 字段跟踪层数,便于调试和业务过滤(如只取前 5 级)
执行过程是迭代而非嵌套,中间结果不落盘
递归 CTE 每轮迭代都基于内存中的前一轮结果集做 JOIN 或过滤,不生成物理临时表。整个过程由优化器统一规划,但深度增大时性能下降明显——因为每层都可能触发一次索引查找或全表扫描。
- 关键字段(如 manager_id、parent_id)务必建索引
- 避免在递归分支里做复杂计算、子查询或字符串拼接
- 非递归逻辑尽量前置(如先过滤再递归)或后置(如最后再 JOIN 关联信息)










