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,还是改用应用层分批拉取。
