1329错误是游标遍历结束的正常信号,因未正确配置continue handler for not found而转为中断错误;它在fetch读空时触发,非语法或权限问题,需确保handler在open前声明、绑定专用done变量且作用域正确。

1329错误不是程序出错,而是游标遍历到末尾的正常信号——没配好CONTINUE HANDLER FOR NOT FOUND,它就会变成中断性错误。
为什么FETCH一读空就报错
MySQL游标本身不带“是否还有下一行”的内置状态。每次FETCH执行时,如果结果集已耗尽,服务器会直接抛出错误码1329(SQLSTATE 02000),而不是返回NULL或FALSE。这和大多数编程语言的迭代器行为相反,容易让人误以为是SQL写错了。
- 错误不是发生在
OPEN或CLOSE,而是在FETCH试图从空游标取值那一瞬间 - 哪怕游标查询本身语法正确、表存在、权限够,只要没数据可取,
FETCH就触发1329 - 这个错误无法靠
IF EXISTS或COUNT(*) > 0提前绕过——游标必须打开后才能知道有没有数据
CONTINUE HANDLER必须紧挨着OPEN前声明
Handler的作用是“捕获NOT FOUND并静默改写状态”,但它有严格作用域规则:必须在OPEN cur之前声明,且每个游标都要独立一套变量+句柄。
- 把
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE写在存储过程开头、所有游标声明之前——无效,不会绑定到任何游标 - 两个游标共用一个
done变量,比如cur1读完设done=TRUE,接着cur2还没打开就被判断为“已结束”——外层循环提前退出 - 正确写法是:先
DECLARE cur1 CURSOR FOR ...,紧接着DECLARE cur1_done INT DEFAULT FALSE,再DECLARE CONTINUE HANDLER FOR NOT FOUND SET cur1_done = TRUE
WHILE循环里FETCH的位置不能错
标准安全结构是“先FETCH → 立即检查done → 再处理数据”,顺序颠倒会导致跳行或重复处理。
- FETCH放在
WHILE开头:第一次循环前没初始化,FETCH读第一行;但第二次循环开始又FETCH一次,跳过第二行 - FETCH放在
WHILE末尾且没检查done就继续循环:最后一行被取出来处理完,再次进入循环体,FETCH触发1329 - 业务逻辑中如果还有
SELECT ... INTO语句,它也会触发同一NOT FOUND句柄——done被意外置为TRUE,建议为游标专用变量命名,如cur_data_done
嵌套游标或动态条件时的常见陷阱
游标声明里的SELECT是静态SQL,不支持运行时拼接参数,强行用CONCAT会语法报错;而嵌套循环若不隔离状态,done变量会互相污染。
- 想按输入参数过滤,直接在
DECLARE CURSOR FOR里写WHERE status = p_status,别尝试CONCAT('SELECT * FROM t WHERE id = ', p_id) - 真要动态SQL(比如表名来自参数),必须用
PREPARE + EXECUTE,但这时已无法配合游标逐行取值,得改用临时表或JSON_TABLE拆解 - 多层
WHILE嵌套时,LEAVE需加标签(如outer_loop: WHILE ...),否则LEAVE默认只跳出最内层
最常被忽略的一点:1329只代表“没数据”,不代表SQL写错或连接失败。它出现时第一反应不该是查语法或权限,而是立刻检查CONTINUE HANDLER是否声明、是否在OPEN前、是否绑定了正确的done变量——三者缺一不可。











