MySQL存储过程不支持递归调用,设max_sp_recursion_depth对自调用无效,必报ERROR 1424;该变量仅限制跨过程嵌套深度,递归应使用WITH RECURSIVE CTE或WHILE+临时表模拟。

MySQL 存储过程根本不允许递归调用,改 max_sp_recursion_depth 对自调用无效,强行写 CALL proc_name() 一定会报 ERROR 1424。 这个变量只管「A→B→C」这种跨过程嵌套调用的深度,不是给你开递归绿灯的。
为什么 SET max_sp_recursion_depth = 100 没用?
你看到网上有人靠这句“解决问题”,大概率是混淆了两种错误:
-
ERROR 1424:语法级禁止——MySQL 解析器在 CALL 阶段就拦截自调用,根本不会走到深度检查; -
ERROR 1456:运行时超限——只发生在合法嵌套链(如 A 调 B、B 调 C)超过设定层数时。
所以如果你的存储过程里写了 CALL my_proc(...),不管 max_sp_recursion_depth 设成多少,都会先卡在 ERROR 1424。这个变量对它完全不生效。
另外注意:SET SESSION max_sp_recursion_depth = 100 无效,必须用 SET GLOBAL max_sp_recursion_depth = 100;但即便设了,也只影响新连接,且重启后丢失。
真正需要递归树形查询,该用 WITH RECURSIVE CTE
MySQL 8.0+ 原生支持递归 CTE,这是唯一被官方认可、语义清晰、性能可控的方案。比如查某个节点的所有子节点:
WITH RECURSIVE sub_tree AS ( SELECT id, parent_id, 0 AS level FROM tree WHERE id = ? UNION ALL SELECT t.id, t.parent_id, st.level + 1 FROM tree t INNER JOIN sub_tree st ON t.parent_id = st.id ) SELECT * FROM sub_tree;
关键点:
- 必须写
RECURSIVE关键字,漏了会报语法错误; - 递归部分必须有终止条件(即
JOIN条件最终无法匹配新行),否则可能无限循环; - 默认最多 1000 层,超限时报错
Recursive query aborted after ... iterations,可临时调高:SET SESSION cte_max_recursion_depth = 5000; - 加超时更稳妥:
SET STATEMENT max_execution_time = 2000 FOR WITH RECURSIVE ...,毫秒级中断比等满层数靠谱。
非要用存储过程封装?只能用 WHILE + 临时表模拟
如果逻辑必须在事务中执行、或要配合 UPDATE/INSERT 等操作,那就放弃“递归过程”幻想,改用迭代模拟:
- 先建临时表:
CREATE TEMPORARY TABLE temp_nodes (id INT PRIMARY KEY); - 插入根节点:
INSERT INTO temp_nodes SELECT id FROM tree WHERE id = ?; - 用
WHILE ROW_COUNT() > 0 DO ... END WHILE循环:每次把temp_nodes中节点的子节点INSERT IGNORE进来; - 退出条件是没新数据插入,而不是靠人工计数——避免漏掉深层节点或误设深度上限。
别在循环里反复 CALL 自己,那只是重复触发 ERROR 1424。临时表方案虽然多几行 SQL,但稳定、可调试、兼容所有 MySQL 8.0+ 场景。
最常被忽略的一点:很多人以为调大 max_sp_recursion_depth 就能绕过限制,其实它连自调用的门都进不去;而真正该调的 cte_max_recursion_depth,又常被当成“存储过程参数”去错地方设。分清这两个变量的作用域和触发时机,比盲目改值重要得多。


















