SQL 游标 CURSOR 高级应用

1次阅读

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

SQL 游标 CURSOR 高级应用

游标 CURSOR 什么时候真该用?

sql 游标不是“循环的替代品”,而是“无法避免逐行处理”的兜底方案。绝大多数场景下,用 JOINGROUP BY、窗口函数或应用层迭代更安全、更快。只有当你必须在数据库内做状态累积(比如动态生成递归路径)、调用带副作用的存储过程(如发消息、写日志表)、或对接不支持集合操作的旧系统时,CURSOR 才是合理选择。

  • 常见错误现象:FETCH NEXT 返回空结果却没检查 @@FETCH_STATUS,导致无限循环或跳过最后一行
  • 使用场景:T-SQL 中批量调用 sp_send_dbmail 给不同用户发定制报表;PL/pgSQL 中构造嵌套 json 对象需逐层拼接
  • 性能影响:游标默认是静态快照(Static),会占用 tempdb 空间;FAST_FORWARD 虽轻量但不支持反向遍历和更新

T-SQL 游标声明的三个关键参数

SQL Server 的 DECLARE CURSOR 有三个实际影响行为的选项,漏掉任一个都可能让逻辑跑偏:

  • LOCAL vs GLOBAL:不加 LOCAL 默认是全局作用域,容易被同名游标覆盖或意外关闭;生产代码务必显式写 LOCAL
  • FORWARD_ONLY vs SCROLL:后者支持 FETCH PRIOR,但开销大且多数业务不需要回溯;95% 场景用 FORWARD_ONLY 就够
  • READ_ONLY vs UPDATE:即使你没打算更新,也建议加 READ_ONLY —— 它禁用键集维护,减少锁和内存占用

postgresqlDECLARE 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_nameDEALLOCATE cursor_name;PG 中 CLOSE cursor_name 即释放,无需额外操作
  • 容易踩的坑:在 try/catch 块里只在 TRYCLOSE,异常发生后游标就泄漏了;务必把 CLOSE/DEALLOCATE 放进 CATCHfinally(若支持)

游标逻辑一旦嵌套多层或混入事务控制,出问题时很难定位是数据状态不对,还是游标位置偏移。上线前至少用小数据集验证 FETCH 的边界行为 —— 特别是空结果集和单行结果集这两种边缘情况。

text=ZqhQzanResources