mysql 8.0递归cte必须显式使用with recursive,锚点与递归部分用union all连接且字段严格对齐,cte须置于join右侧,终止条件必须在递归分支where中声明。

因为递归 CTE 把树形遍历这件事从“应用层拼逻辑”或“多层 JOIN 套娃”里拎出来,交给了 MySQL 查询引擎原生处理——它不是“看起来简洁”,而是执行路径更可控、结果更确定。
WITH RECURSIVE 是强制语法,漏写就根本跑不起来
MySQL 8.0 不接受 WITH dept_tree AS () 这种写法,哪怕逻辑再对,也会报 ERROR 1223 (HY000): Can't execute the query because it contains recursive WITH。必须显式写成 WITH RECURSIVE dept_tree AS ()。这不是风格问题,是解析器在语法校验阶段就拦截:没 RECURSIVE,连执行计划都不会生成。
锚点和递归部分必须用 UNION ALL,且字段严格对齐
常见错误是用 UNION 替代 UNION ALL,或者让两部分 SELECT 返回列数/类型不一致。比如锚点写 SELECT id, name, 1 AS level,递归部分却漏了 + 1 或用了 CAST(name AS TEXT) 而锚点是 VARCHAR(50)。这会导致:
- 隐式类型转换失败,查询直接中断
-
UNION去重破坏轮次迭代的中间状态,某一层本该有 3 行,去重后只剩 1 行,后续递归就断了 - 字段顺序错位(如锚点是
id, name, parent_id,递归部分写成name, id, parent_id),MySQL 会按位置匹配,结果列语义错乱
递归引用只能出现在 JOIN 右侧,不能进子查询或 LEFT JOIN 右表
这是最容易被忽略的执行模型限制。写成 FROM employees e JOIN subordinates s ON e.manager_id = s.emp_id 是合法的;但若调换顺序:FROM subordinates s LEFT JOIN employees e ON e.manager_id = s.emp_id,立刻报 ERROR 3641 (HY000): Recursive reference to CTE 'subordinates' is not allowed in this context。原因在于 MySQL 每轮迭代都以上一轮 CTE 输出为驱动表,去查基础表,不能反过来用基础表驱动 CTE。
同样,WHERE emp_id IN (SELECT emp_id FROM subordinates) 这类子查询也禁止——递归 CTE 不可出现在 WHERE 的标量子查询中。
终止条件必须写在递归分支的 WHERE 里,level 字段不是可选配件
MySQL 不会自动停,它只看“本轮递归 SELECT 是否返回空集”。没写 WHERE,或者条件放错位置(比如写在主查询里),就会触发 Recursive query aborted after 1000 iterations。典型写法是:
- 锚点设
level = 1 - 递归部分写
s.level + 1 AS level - 递归部分的
WHERE加上s.level 或 <code>e.manager_id IS NOT NULL等真实业务约束
防环也得靠这个:比如加 AND e.id != e.manager_id 避免自循环,否则数据脏了都查不出来。
真正难的不是写出第一版递归 CTE,而是理解它每一轮都在做什么、中间结果长什么样——建议先用 SELECT * FROM subordinates 查出完整展开树,再逐步加 WHERE 和 level 控制范围。不然调试时你看到的错误,往往不是语法错,而是逻辑轮次失控。











