mysql函数不支持递归调用,但8.0+可用with recursive cte实现递归查询;其需锚定成员与递归成员,依赖索引、终止条件和union all,深度默认1000,须防性能陷阱。

MySQL 的内置函数本身不支持递归调用——比如你不能写一个 CONCAT 或 IF 函数内部再调用自己。但 MySQL 8.0+ 支持通过 WITH RECURSIVE 语法实现「递归查询」,这是 SQL 层面的递归能力,不是函数级的。
MySQL 函数体内不能递归调用自身
你无法在自定义函数(CREATE FUNCTION)中直接写 RETURN my_func(...) 来实现递归。MySQL 会报错:ERROR 1424 (HY000): Recursive stored functions and triggers are not allowed.
- 这是硬性限制,和语言设计有关,不是版本问题
- 哪怕你用命名函数表达式或存储过程模拟,也绕不过这个限制
- 想“递归计算”,只能靠
WITH RECURSIVECTE 替代,或者把逻辑移到应用层(PHP/Python/Java)
真正可用的递归:WITH RECURSIVE CTE(MySQL 8.0+)
这才是你在 MySQL 里处理树形结构、组织架构、分类目录等场景的实际工具。它不是函数,而是一种查询构造方式,但效果等价于“SQL 层递归”。
- 必须包含两部分:
锚定成员(初始数据) +递归成员(用UNION ALL连接,且必须引用 CTE 自身) - 终止条件靠
WHERE子句控制,例如level 或 <code>manager_id IS NOT NULL - 默认最大递归深度是 1000,超限会报错
ERROR 3636 (HY000): Recursive query aborted after 1001 iterations;可临时调大:SET SESSION cte_max_recursion_depth = 2000;
常见误用与性能陷阱
很多人以为写了 WITH RECURSIVE 就万事大吉,结果查 10 万行分类表时卡死或超内存。
- 没加索引:确保
manager_id、parent_id等关联字段有索引,否则每次递归都要全表扫描 - 没设终止条件或条件太宽:比如写成
WHERE e.parent_id > 0却没排除环状引用,可能触发无限循环(虽有深度限制兜底,但已浪费大量资源) - 误用
UNION而非UNION ALL:CTE 递归必须用UNION ALL,用UNION会强制去重,性能暴跌且可能报错 - 跨版本兼容问题:MySQL 5.7 及更早版本完全不支持
WITH RECURSIVE,只能用自连接模拟(最多支持固定层数),别指望“写一次跑通所有环境”
真正要小心的,不是“能不能递归”,而是“递归时有没有意识到它是一次嵌套 N 层的 JOIN”。每一层都在放大中间结果集,稍不注意,10 层深度就能让结果膨胀几十倍。实际项目里,建议先用 EXPLAIN FORMAT=TREE 看执行计划,再决定是否真要用递归 CTE,还是改用应用层分批拉取。










