mysql 8.0+ 不支持递归存储过程,因call自调用被明确禁止;真需求应使用递归cte(如with recursive)封装树形查询,或用while循环+临时表模拟,max_sp_recursion_depth仅控制嵌套深度,与递归无关。

MySQL 8.0+ 怎么写递归存储过程
MySQL 8.0 开始原生支持递归 WITH(CTE),但**存储过程本身不支持递归调用**——你不能在存储过程中直接用 CALL 调用自己,会报错 ERROR 1424 (HY000): Recursive stored procedures are not allowed。
所以“递归存储过程”实际是伪命题。真需求通常是:遍历树形结构(如部门、分类、评论回复链),而你想把它封装成可复用的逻辑。可行做法是用**递归 CTE + 存储过程封装查询逻辑**,或用循环模拟递归。
- 推荐优先用递归 CTE:简洁、安全、性能好,例如
WITH RECURSIVE t AS (...) SELECT * FROM t - 若必须用存储过程(比如要配合事务、多步处理),就用
WHILE循环 + 临时表模拟递归:先把根节点插入临时表,再循环把子节点逐层 JOIN 进来 - 别硬写
CALL proc_name(...)自调用——MySQL 明确禁止,不是配置能绕过的
max_sp_recursion_depth 是干啥的,设了有用吗
max_sp_recursion_depth 控制的是「存储过程嵌套调用深度」,不是递归调用。比如 A 调 B,B 调 C,C 调 D……总共 4 层,就计为 4。它**完全不影响递归 CTE,也不允许你开启存储过程自调用**。
默认值是 0,表示“不限制嵌套深度”,但实际受栈空间限制;设成非 0 值(如 SET GLOBAL max_sp_recursion_depth = 10)只是给嵌套加个软上限,超了报 ERROR 1456 (HY000): Recursive limit 10 was exceeded。
- 这个变量对“想写递归存储过程”的人毫无帮助——它管不了自调用,只管 A→B→C 这种线性嵌套
- 修改需
GLOBAL权限,且新连接才生效;当前会话用SET SESSION无效 - 设太高可能耗尽 stack,设太低容易误伤正常嵌套逻辑(比如 ORM 自动生成的多层过程调用)
替代方案:用递归 CTE 封装成视图或预编译语句
真正该花时间的地方,是把树形查询逻辑写成可复用的递归 CTE,而不是折腾存储过程递归。CTE 支持参数化(通过外部变量或准备语句),也支持在存储过程中作为查询主体使用。
例如查某个节点的所有后代:
WITH RECURSIVE category_path AS ( SELECT id, name, parent_id, 0 AS level FROM categories WHERE id = ? UNION ALL SELECT c.id, c.name, c.parent_id, cp.level + 1 FROM categories c INNER JOIN category_path cp ON c.parent_id = cp.id ) SELECT * FROM category_path;
- 把
?换成具体 ID 就能直接执行;放进存储过程里,用PREPARE+EXECUTE可实现参数化 - 注意:CTE 中不能引用存储过程变量(如
v_root_id),必须用参数占位符或拼接字符串(后者慎防注入) - 递归 CTE 默认最大 1000 层,超了会报
ERROR 3636 (HY000): Recursive query aborted after 1001 iterations,可通过cte_max_recursion_depth系统变量调整(全局或会话级)
为什么很多人卡在这儿:混淆了“递归查询”和“递归过程”
MySQL 的文档和错误提示都明确区分了「recursive CTE」和「recursive stored procedure」。前者是标准 SQL 功能,后者被设计为禁止项。很多搜索“mysql 递归存储过程”的人,其实是想要一个带参数、可复用的树查询封装,结果掉进“试图解禁自调用”的坑里。
- 看到
max_sp_recursion_depth就以为是开关——其实它连门锁都不是,只是楼梯间标了个“最多上 10 层”的牌子 - 在低版本 MySQL(
- 即使在 8.0+,如果业务要求“每查一层就更新一次状态表”,那必须用循环模拟,此时重点是临时表索引、循环退出条件、避免无限循环,而不是纠结递归深度配置
递归逻辑的边界和终止条件,永远比配置值更关键。写漏 WHERE 或 JOIN 条件,比没设 cte_max_recursion_depth 更容易让服务器卡住。











