CLOSE和DEALLOCATE必须成对使用:CLOSE仅释放结果集和位置锁,保留游标结构;DEALLOCATE才销毁对象、释放内存和句柄,二者语义分离、不可替代。

CLOSE 和 DEALLOCATE 必须成对使用,缺一不可。只 CLOSE 不 DEALLOCATE 会导致后续执行报错 消息16915,级别16,状态1(游标已存在)或 消息16905(游标已打开);只 DEALLOCATE 不 CLOSE 在 SQL Server 中虽不报错,但可能引发资源未及时释放、锁残留或事务中游标意外关闭等问题。
为什么不能只用 CLOSE 或只用 DEALLOCATE
CLOSE 仅释放结果集和游标位置锁,保留游标定义结构;DEALLOCATE 才真正销毁游标对象、释放内存及系统句柄。SQL Server 中二者语义分离,且作用不可替代。
常见错误现象:
- 重复执行存储过程时抛出
消息16915:因上次的游标名仍被注册,DECLARE失败 - 在事务内循环读取时突然报
游标未打开:默认CURSOR_CLOSE_ON_COMMIT = ON,COMMIT后自动CLOSE,但未DEALLOCATE,下次OPEN前若未重新DECLARE就会失败 - 长时间运行后连接内存占用升高:大量已
CLOSE但未DEALLOCATE的游标堆积在会话上下文中
CLOSE 和 DEALLOCATE 的执行顺序与作用域
必须先 CLOSE,再 DEALLOCATE;顺序颠倒虽不报错,但 DEALLOCATE 对已关闭游标无副作用,逻辑上不合理且易掩盖问题。
作用域差异:
-
CLOSE cursor_name:适用于显式命名的游标(DECLARE cursor_name CURSOR) -
DEALLOCATE cursor_name:同样针对命名游标;若为游标变量(DECLARE @cur CURSOR),需用DEALLOCATE @cur -
LOCAL游标在批处理/存储过程退出时自动DEALLOCATE,但显式调用仍是最佳实践 -
GLOBAL游标不会自动释放,必须显式CLOSE+DEALLOCATE
事务中游标自动关闭的坑:CURSOR_CLOSE_ON_COMMIT
SQL Server 默认开启 CURSOR_CLOSE_ON_COMMIT,即每次 COMMIT 或 ROLLBACK 后自动执行 CLOSE —— 但不会 DEALLOCATE。这导致游标处于“已关闭但未释放”状态,下次 OPEN 会失败。
解决方式:
- 在存储过程开头加
SET CURSOR_CLOSE_ON_COMMIT OFF,避免事务干扰游标生命周期 - 确保每次
OPEN前,游标已DEALLOCATE并重新DECLARE(尤其在循环调用场景) - 不要依赖“自动关闭”来省略
CLOSE—— 它不可控,且不释放资源
MySQL / Oracle / SQL Server 的语法差异
跨数据库迁移时容易栽跟头:
- SQL Server:
CLOSE cursor_name; DEALLOCATE cursor_name;(两步明确分离) - MySQL:
CLOSE cursor_name;即完成关闭与释放(无DEALLOCATE语句),但需配合DECLARE CONTINUE HANDLER FOR NOT FOUND正确退出循环 - Oracle:
CLOSE cursor_name;关闭,DEALLOCATE不适用;若用游标变量,需NULL赋值或作用域结束自动释放;部分版本支持DBMS_SQL.CLOSE_CURSOR - PostgreSQL:无显式游标释放语法,
CLOSE后即释放,但需注意DECLARE的游标作用域限制
最危险的是把 SQL Server 的写法直接套到 MySQL 存储过程中——多写 DEALLOCATE 会报语法错误;反过来,在 SQL Server 里漏掉 DEALLOCATE 则埋下资源泄漏隐患。

















