根本原因是CONTINUE HANDLER作用域为整个存储过程体,共用done变量导致内层FETCH触发后误判外层结束;必须为每层游标声明独立done变量、专属HANDLER,并在OPEN前重置为FALSE。

为什么外层循环总被内层 FETCH 提前终止?
根本原因不是游标“没数据”,而是 DECLARE CONTINUE HANDLER FOR NOT FOUND 作用域是整个存储过程体,不区分游标层级。只要任意一个 FETCH 遇到空结果集,就会触发 handler 并设置全局 done 变量为 TRUE,导致外层循环误判结束。
实操建议:
- 每个游标必须配独立的状态变量(如
done_outer、done_inner、done_innermost),严禁共用同一个done - 每个游标声明后,立即紧跟专属的
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done_xxx = TRUE - 内层游标
OPEN前,务必重置其对应done_xxx = FALSE;否则上一轮残留的TRUE会让本次FETCH直接跳过整轮循环
CLOSE 和 OPEN 的顺序与限制
MySQL 游标不支持 reopen——CLOSE 后再 OPEN 会报错 ERROR 1326 (HY000): Cursor is not open。这不是警告,是硬性中断。
常见错误场景:
- 在内层循环中多次
OPEN同一个游标(比如写在WHILE内部但没CLOSE) - 外层循环未
CLOSE就直接进入下一轮,导致下次OPEN失败 - 异常退出路径(如
LEAVE或EXIT)遗漏CLOSE,造成资源泄漏
正确做法:每个 OPEN 必须有且仅有一个对应的 CLOSE,且应放在该游标作用域末尾(如 END LOOP 后、END 前),或用 DECLARE EXIT HANDLER 统一兜底。
跨层 LEAVE 标签怎么写才有效?
MySQL 不识别缩进或注释里的“外层”“内层”。LEAVE 后面跟的标签名必须真实存在,且定义位置必须能被当前作用域访问到。
典型错误:
- 在
inner_loop: LOOP内写LEAVE outer_loop,但outer_loop标签定义在更外层的BEGIN块里,而当前作用域是嵌套BEGIN,不可见 - 标签名与变量/游标同名,触发
ERROR 1305 (42000): PROCEDURE does not exist - 大小写混用(如定义
Outer_Loop却写LEAVE outer_loop),MySQL 默认区分大小写
安全写法:
- 把最外层标签(如
main_loop:)定义在存储过程体开头的BEGIN后 - 所有嵌套块都用
BEGIN ... END显式包裹,并在内部重新声明所需变量和 handler -
LEAVE只用于跳出**当前作用域最近的标签**;真需跨多层,改用标志变量 +ITERATE或提前RETURN
三层及以上嵌套时最容易被忽略的细节
每增加一层嵌套,变量作用域、handler 作用域、游标生命周期就多一层耦合。调试时往往只看到最后一层失败,实际根因在第二层的 done 未重置或第一层的 CLOSE 遗漏。
关键检查点:
- 是否为每一级游标都声明了独立
done变量,并在OPEN前显式设为FALSE - 是否所有
OPEN都有对应CLOSE,且不在同一作用域内重复OPEN - 是否所有
LEAVE引用的标签都在当前BEGIN块或其外层BEGIN中正确定义 - 是否在嵌套
BEGIN内重新声明了本层需要的变量(不能依赖外层同名变量)
嵌套越深,越要放弃“靠缩进猜逻辑”的习惯——MySQL 只认显式声明和标签定义,其余全是幻觉。


















