MySQL 5.7中CLOSE即完成游标释放,DEALLOCATE不合法且报错;游标资源在OPEN时分配、CLOSE时彻底释放,必须在循环结束后显式执行一次CLOSE,且NOT FOUND句柄须紧随游标声明。

CLOSE 就够了,DEALLOCATE 在 MySQL 5.7 中不合法且会报错。 不要照搬 SQL Server 或 Oracle 的写法,MySQL 的游标生命周期管理模型完全不同。
为什么不能用 DEALLOCATE cursor_name
MySQL 5.7(及所有已知版本)**根本不支持 DEALLOCATE cursor_name 语法**。如果你写了这句,执行时直接报错:ERROR 1064 (42000): You have an error in your SQL syntax。部分资料提到的 DEALLOCATE PREPARE 是针对预处理语句(PREPARE / EXECUTE)的,和游标无关,混用会导致语法错误。
MySQL 的设计是:游标在 CLOSE 后即释放全部资源(内存、锁、内部句柄),无需二次销毁。
-
CLOSE cursor_name是唯一且必须的释放动作 - 声明游标(
DECLARE cursor_name CURSOR FOR ...)本身不分配运行时资源,只注册定义 - 真正占用资源的是
OPEN操作;CLOSE后该游标名可被重复OPEN(只要没重新DECLARE)
CLOSE 必须放在 FETCH 循环结束后,且不能遗漏
漏掉 CLOSE 不会立即报错,但会导致连接级资源缓慢泄漏——尤其在高并发调用存储过程时,可能引发“Too many connections”或内存持续增长。
常见错误写法是把 CLOSE 放在循环体里、或用 LEAVE 跳出后忘记补上:
- 错误:在
IF done THEN LEAVE read_loop;后没写CLOSE,直接结束过程 - 错误:把
CLOSE写在END LOOP外面但没加标签跳转,导致循环中多次执行CLOSE(会报ERROR 1326 (HY000): Cursor is not open) - 正确位置:紧接在
END LOOP之后、END之前,且只执行一次
推荐结构:
OPEN cur;
read_loop: LOOP
FETCH cur INTO @id, @name;
IF done THEN
LEAVE read_loop;
END IF;
-- 处理逻辑
END LOOP;
CLOSE cur; -- 这里是唯一且确定的位置NOT FOUND 句柄必须紧接游标声明后,且 done 变量顺序不能错
MySQL 对声明顺序极其敏感。若 DECLARE CONTINUE HANDLER FOR NOT FOUND 没紧跟在 DECLARE cursor_name CURSOR 后面,或 done 变量声明在游标之后、句柄之前,都会报 ERROR 1337 (42000)。
-
done必须是INT DEFAULT FALSE或BOOLEAN DEFAULT FALSE(MySQL 5.7 支持BOOLEAN作为别名) - 句柄只能捕获
NOT FOUND(SQLSTATE '02000'),用SQLWARNING或SQLEXCEPTION无法触发退出 - 第一次
FETCH若无数据,done立即为TRUE,循环体零次执行——这是正常行为,不是 bug
事务中游标不会自动关闭,也不受 COMMIT 影响
MySQL 的游标生命周期完全独立于事务。无论你是否在事务块中(START TRANSACTION / COMMIT),CLOSE 都必须显式调用,且 COMMIT 不会触发任何隐式关闭。
这点和 SQL Server 的 CURSOR_CLOSE_ON_COMMIT = ON 截然不同——MySQL 没有这个开关,也不存在“已关闭未释放”的中间状态。
- 事务内多次
OPEN/CLOSE游标完全安全 - 不要在事务里靠
COMMIT来“清理”游标,它根本不管这事 - 如果存储过程主体在事务中,确保
CLOSE语句在COMMIT或ROLLBACK之后(或之前),但逻辑上必须存在
最易忽略的一点:游标变量(DECLARE @cur CURSOR)在 MySQL 中不支持——所有游标必须显式命名,且作用域限于当前存储过程。这意味着你没法用变量间接引用游标,也就不存在“动态 DEALLOCATE”的需求。


















