mysql游标必须配not found处理器,否则易致无限循环或跳过末行;需在open前声明declare continue handler for not found set done = true,且done初始值须为false。

MySQL游标必须配NOT FOUND处理器,否则循环失控
MySQL游标没有自动检测末尾的机制,FETCH失败后@@FETCH_STATUS可能不及时更新或行为不稳定,仅靠它判断会导致无限循环或跳过最后一行。常见错误是漏写处理器,直接用WHILE @@FETCH_STATUS = 0开干。
必须在DECLARE cursor_name CURSOR FOR之后、OPEN之前,加一句:DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;(其中done是你自己定义的BOOLEAN或INT变量)。
注意:done初始值必须为FALSE(或0),否则空结果集时循环直接跳过;如果游标查询本身可能返回零行,这点极易被忽略。
UPDATE不能用WHERE CURRENT OF,必须显式主键定位
MySQL根本不支持UPDATE ... WHERE CURRENT OF cursor_name语法,照搬SQL Server或Oracle写法会直接报错:ERROR 1325 (HY000): Cursor is not open或Unknown column 'cursor_name' in 'where clause'。
所有修改必须显式用主键或唯一标识字段做WHERE条件,例如:UPDATE users SET status = 'processed' WHERE id = @user_id;
- 原查询里没选主键?必须提前加上,否则无法安全定位当前行
- 高并发下防重复处理:在
UPDATE里加条件限制,如WHERE id = @user_id AND status = 'pending',再用ROW_COUNT()确认是否真改了一行 - 别在游标循环里做
COMMIT——每批独立事务即可,嵌套START TRANSACTION反而增加锁冲突风险
嵌套游标会让性能断崖式下跌,优先用临时表+JOIN替代
单层游标处理10万行可能几秒完成,一旦外层查用户、内层查该用户订单,复杂度立刻变成O(n×m)。10万用户 × 平均10单 = 100万次FETCH,实际执行常卡住数分钟甚至超时。
监控SHOW PROCESSLIST,看到状态长期停留在Sending data或Copying to tmp table,基本就是嵌套游标反复打开/关闭内层游标导致的。
- 避免在
WHILE循环体内反复DECLARE + OPEN新游标 - 改用临时表预存内层结果,再用
JOIN或单层游标处理 - 如果内层只是聚合(如求总金额),优先用子查询或窗口函数,而不是游标
分批游标比LIMIT更可靠,但要注意READ ONLY和CLOSE
PostgreSQL服务端游标(DECLARE c CURSOR FOR ...)不把全部结果集拉到客户端内存,且FETCH FORWARD 1000能精确取数,不受并发DML导致的LIMIT OFFSET错位影响,比纯WHERE id > ? ORDER BY id LIMIT N更稳。
但容易被忽略的是资源管理:CLOSE c必须显式调用,否则长期运行可能耗尽游标句柄(max_cursor有限制);若业务允许,声明时加READ ONLY,避免事务膨胀。
跨库通用的坑在于状态一致性——某批成功、下一批失败时,中间状态怎么续跑?MySQL没内置游标断点恢复,得靠外部记录最后处理的id值,再从那里重启。










