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

MySQL 8.0 的递归 CTE 不是“能用”,而是处理组织架构这类树形数据的**唯一合理路径**——子查询套嵌、存储过程或应用层拼接,都会在可维护性、性能或正确性上出问题。
WITH RECURSIVE 是硬性语法要求,漏写就报错
你不能只写 WITH dept_tree AS (...) 就指望它递归。MySQL 会直接拒绝执行,报错 ERROR 1223 (HY000): Can't execute the query because it contains recursive WITH。
- 必须显式写成
WITH RECURSIVE dept_tree AS (...) - 哪怕你只在递归部分用了自引用,锚点(初始)部分没引用,也必须加
RECURSIVE - 这不是风格建议,是解析器层面的校验;不加就根本进不了执行阶段
锚点和递归部分必须用 UNION ALL,且字段严格对齐
常见错误是把 UNION 当成通用连接符,或者让两个 SELECT 返回列数/类型不一致,比如锚点返回 id, name, level,递归部分却漏了 level + 1 或用了 CAST(name AS TEXT) 而锚点是 VARCHAR(50)。
- 只能用
UNION ALL:递归引擎按轮次迭代,UNION去重会破坏中间结果一致性 - 显式声明列名最安全:
WITH RECURSIVE tree (id, name, parent_id, level) AS (...) - 递归部分的
SELECT必须和锚点字段数、顺序、类型完全一致;类型不一致时优先用CAST对齐,别依赖隐式转换
递归部分的 JOIN 必须把 CTE 放在右边
写成 FROM departments d JOIN dept_tree dt ON d.parent_id = dt.id 是对的;但若写成 FROM dept_tree dt JOIN departments d ON d.parent_id = dt.id,会立刻报错 ERROR 3641 (HY000): Recursive reference to CTE 'dept_tree' is not allowed in this context。
- MySQL 执行模型强制要求:每轮迭代以上一轮 CTE 输出为驱动表,去关联基础表
- CTE 不能出现在
LEFT JOIN的右表位置,也不能出现在WHERE子查询里(如WHERE id IN (SELECT ... FROM dept_tree)) - 终止条件必须写在递归部分的
WHERE中,比如WHERE dt.level 或 <code>WHERE d.parent_id IS NOT NULL AND d.id != d.parent_id
层级控制与防环必须手动做,不能靠“查够了就停”
MySQL 不会因为你查到第 100 行就自动终止;它按轮次执行,直到某一轮递归查询返回空集才停。如果没写好 WHERE,很容易触发 Recursive query aborted after 1000 iterations。
-
level字段是标配:锚点设为 1,递归部分写dt.level + 1,再加WHERE dt.level - 防自环更关键:仅靠
parent_id IS NOT NULL不够,得加路径检查,比如用CONCAT(path, '/', id)初始化路径,再用NOT LIKE CONCAT('%/', id, '/%')过滤 -
cte_max_recursion_depth是兜底手段,不是解决方案;临时调高只是帮你看到错误在哪,不是修复逻辑
真正容易被忽略的是:递归 CTE 的“中间结果”不可见,调试只能靠拆解——先单独跑锚点查询,再拿它的输出手工代入递归部分验证逻辑。一旦路径或 JOIN 条件写反,结果就静默错乱,不会报错。











