mysql 8.0存储过程中游标使用必须严格遵循声明顺序:变量(含done)→游标→not found句柄→open→fetch后立即判done→close,顺序错即报错,且不可依赖until退出循环。

MySQL 8.0 存储过程中无法“优雅”地遍历游标——游标本身是反模式的妥协方案,所谓“优雅”只能体现在结构清晰、不漏数据、不卡死、易排查,而不是写得漂亮。
DECLARE 游标必须放在变量声明之后、HANDLER 之前
顺序错一个字,CREATE PROCEDURE 就会报语法错误或静默失败。MySQL 8.0 对声明顺序极其敏感,不是警告,是硬性约束。
-
DECLARE变量(含done标志)必须在最前 -
DECLARE CURSOR必须紧接变量之后,不能穿插其他逻辑 -
DECLARE CONTINUE HANDLER FOR NOT FOUND必须在游标声明之后、OPEN之前 - 游标
SELECT中不能含用户变量(如@var)、不能拼表名或列名,否则OPEN时直接报错ERROR 1327 (42000): Undeclared variable
FETCH 后必须立刻检查 done,不能等下一轮循环再判
很多人用 REPEAT ... UNTIL done,但第一次 FETCH 前 done 是 FALSE,而空结果集会导致第一次 FETCH 就触发 NOT FOUND、done 置 TRUE,此时若没立即跳出,后续逻辑仍会执行一次(比如多插一条空流水、多更新一次状态)。
- 正确做法:每次
FETCH后紧跟IF done THEN LEAVE loop_label; END IF; - 别依赖
UNTIL done的末尾判断——它只管“退出”,不管“跳过本次” - 示例中常见错误:
FETCH cur INTO v_id, v_amt; UPDATE orders SET status = 'settled' WHERE id = v_id;—— 若v_id未被赋值(即FETCH已越界),这条UPDATE会变成WHERE id = NULL,静默不生效,还难定位
动态参数游标只能靠用户变量 + 静态 SQL 模拟
MySQL 不支持 Oracle 那种带形参的游标(CURSOR c(p_id INT) IS SELECT ... WHERE id = p_id)。想让游标“接收运行时参数”,唯一办法是先给用户变量赋值,再在游标 SELECT 中引用它。
- 必须用
SET @ftaskid = 'xxx';赋值,不能用SELECT ... INTO @ftaskid后立刻声明游标——声明阶段不求值,OPEN时才执行SELECT,此时变量必须已存在且有值 - 游标定义里写
WHERE ftaskid = @ftaskid是合法的,但要注意:该变量作用域是会话级,若存储过程并发执行,可能被覆盖(尤其用连接池时) - 更安全的做法:把动态条件提前塞进临时表,再让游标查这张临时表,避免变量污染
游标遍历完必须 CLOSE,且不能依赖自动释放
MySQL 8.0 不会在存储过程退出时自动释放游标资源。如果忘了 CLOSE,下次调用同一过程时可能遇到 ERROR 1326 (HY000): Cursor is already open,尤其在调试反复 CALL 时极易复现。
-
CLOSE要放在LEAVE之后、END之前,确保任何退出路径(包括异常)都经过它 - 别指望
DECLARE EXIT HANDLER来兜底——它只捕获 SQL 异常,不捕获逻辑跳转(如LEAVE) - 复杂分支多时,建议用
BEGIN ... END块包裹整个游标逻辑,并在块末尾统一CLOSE
真正容易被忽略的点:游标性能和替代方案。哪怕你把每一步都写对了,5 万行数据用游标逐条 INSERT + UPDATE,耗时可能是等价 INSERT ... SELECT + UPDATE ... JOIN 的 20 倍以上。游标不该是默认选择,而应是「实在没法写成集合操作」时的最后一道工序。











