MySQL存储过程中游标使用必须严格遵循四步顺序:声明、打开、FETCH+判断退出、关闭;须配CONTINUE HANDLER FOR NOT FOUND防报错1329,且每个游标需独立done变量与句柄,避免嵌套冲突和数据不一致。

MySQL 存储过程中用 WHILE 配合 CURSOR 处理数据,不是“能用就行”,而是必须严格按顺序初始化、声明句柄、控制退出条件,否则循环会提前终止或无限卡住。
游标必须配 CONTINUE HANDLER FOR NOT FOUND 才能安全退出
不加句柄时,FETCH 读到结果集末尾会直接报错(错误码 1329),整个存储过程中断。这不是“异常情况”,而是游标遍历的必然终点。
-
DECLARE done INT DEFAULT FALSE;—— 必须用INT或BOOLEAN类型,不能用VARCHAR或未初始化变量 -
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;—— 必须在OPEN cur之前声明,且只能声明一次 - 句柄只捕获
NOT FOUND,对其他 SQL 错误(如除零、字段超长)无效,需额外加SQL EXCEPTION句柄
WHILE 循环体里不能漏掉 FETCH,也不能放错位置
常见错误是把 FETCH 放在循环开头,导致第一行被跳过;或者放在末尾却没判断 done 就再次进入循环体,引发重复处理最后一行。
- 标准写法是:先
FETCH→ 立即检查IF done THEN LEAVE ...→ 再执行业务逻辑 - 不要在循环体中间再写一次
FETCH,除非你明确需要“预读下一行” - 如果业务逻辑里有
SELECT语句且可能返回空结果,它也会触发NOT FOUND句柄——这会意外改变done值,建议为游标单独声明句柄变量(如cur_done),避免污染
游标查询语句本身不能含动态参数,WHERE 条件要提前拼好
CURSOR 声明中的 SELECT 是静态 SQL,不支持直接传参。想根据输入参数过滤,得用字符串拼接 + PREPARE,但那样就脱离了常规游标用法,复杂度陡增。
- 正确做法:把参数作为存储过程
IN参数,在DECLARE CURSOR FOR的WHERE中直接引用,例如SELECT id, name FROM users WHERE status = p_status - 错误做法:试图写成
DECLARE cur CURSOR FOR CONCAT('SELECT ... WHERE id = ', p_id)—— 语法不合法 - 如果真要动态 SQL,必须改用
PREPARE + EXECUTE,但此时已无法用游标逐行取值,得靠临时表或 JSON 拆解
嵌套循环时,每个游标都要有自己的 done 和句柄
一个存储过程中多个游标共用同一个 done 变量,会导致外层循环因内层游标结束而提前退出。
- 为每个游标声明独立变量:例如
cur1_done、cur2_done - 每个
DECLARE CONTINUE HANDLER必须紧跟对应游标声明之后,不能集中写在开头 - 嵌套
WHILE时,外层循环的LEAVE标签名必须唯一,比如outer_loop: WHILE ... DO,内层用inner_loop: WHILE ... DO
真正容易被忽略的点是:游标打开后,查询结果集就被固化了;后续对原表的修改(如其他事务更新了某行)不会反映在当前游标中。如果你依赖实时数据一致性,游标不是合适选择——这时候该用带事务控制的单条 UPDATE 或分页 SELECT ... LIMIT 配合应用层处理。


















