游标仅在必须数据库内逐行处理时才该用,如状态累积、调用有副作用的存储过程或对接不支持集合操作的旧系统;其余场景优先用join、group by等集合操作。

游标 CURSOR 什么时候真该用?
sql 游标不是“循环的替代品”,而是“无法避免逐行处理”的兜底方案。绝大多数场景下,用 JOIN、GROUP BY、窗口函数或应用层迭代更安全、更快。只有当你必须在数据库内做状态累积(比如动态生成递归路径)、调用带副作用的存储过程(如发消息、写日志表)、或对接不支持集合操作的旧系统时,CURSOR 才是合理选择。
- 常见错误现象:
FETCH NEXT返回空结果却没检查@@FETCH_STATUS,导致无限循环或跳过最后一行 - 使用场景:T-SQL 中批量调用
sp_send_dbmail给不同用户发定制报表;PL/pgSQL 中构造嵌套 json 对象需逐层拼接 - 性能影响:游标默认是静态快照(
Static),会占用 tempdb 空间;FAST_FORWARD虽轻量但不支持反向遍历和更新
T-SQL 游标声明的三个关键参数
SQL Server 的 DECLARE CURSOR 有三个实际影响行为的选项,漏掉任一个都可能让逻辑跑偏:
-
LOCALvsGLOBAL:不加LOCAL默认是全局作用域,容易被同名游标覆盖或意外关闭;生产代码务必显式写LOCAL -
FORWARD_ONLYvsSCROLL:后者支持FETCH PRIOR,但开销大且多数业务不需要回溯;95% 场景用FORWARD_ONLY就够 -
READ_ONLYvsUPDATE:即使你没打算更新,也建议加READ_ONLY—— 它禁用键集维护,减少锁和内存占用
postgresql 的 DECLARE cursor_name CURSOR 和 PL/pgSQL 实际差异
PostgreSQL 不像 SQL Server 那样把游标当第一类对象管理。它的游标本质是“命名查询计划句柄”,生命周期绑定到事务或函数作用域:
- 使用场景:在
DO块或函数里打开游标后,只能用FETCH取固定行数(如FETCH 10 IN my_cursor),不支持类似 T-SQL 的FETCH NEXT循环语法 - 参数差异:没有
LOCAL/GLOBAL概念;WITH HOLD允许跨事务保持(仅限非事务块中定义的游标),但会锁住快照数据,慎用 - 兼容性影响:如果迁移 SQL Server 游标逻辑到 PG,别直接套用
while @@FETCH_STATUS = 0结构 —— 改用LOOP ... EXIT WHEN NOT FOUND;更自然
游标最常被忽略的资源释放陷阱
游标不是自动回收的。哪怕函数正常退出,没显式 CLOSE + DEALLOCATE,它仍占着连接上下文和内存:
- 常见错误现象:存储过程重复执行后报错
The cursor does not exist.或Cursor name 'xxx' is already in use.,其实是上次没清理干净 - 实操建议:T-SQL 必须成对使用
CLOSE cursor_name和DEALLOCATE cursor_name;PG 中CLOSE cursor_name即释放,无需额外操作 - 容易踩的坑:在
try/catch块里只在TRY里CLOSE,异常发生后游标就泄漏了;务必把CLOSE/DEALLOCATE放进CATCH或finally(若支持)
游标逻辑一旦嵌套多层或混入事务控制,出问题时很难定位是数据状态不对,还是游标位置偏移。上线前至少用小数据集验证 FETCH 的边界行为 —— 特别是空结果集和单行结果集这两种边缘情况。