隐式游标仅在明确单行查询时更快,因自动优化且无内存开销;多行场景会报ORA-01422错误,强行循环调用反而更慢。
隐式游标什么时候快,什么时候慢
隐式游标(比如 select ... into 或 update 语句)在单行操作时几乎总是更快——oracle 自动优化、不占 pga 内存、无声明开销。但一旦涉及多行结果,它就直接报错:ora-01422: exact fetch returns more than requested number of rows。这不是性能问题,是语法限制。
所以“快”只存在于明确知道最多返回一行的场景,比如查主键、查配置表唯一键值。如果业务逻辑本就需要遍历多行,硬用隐式游标去套循环(比如反复 SELECT ... INTO),反而比显式游标慢得多——每次执行都重新解析、硬软解析开销叠加,还容易触发 latch 竞争。
- 适合隐式游标:单行查询、DML 影响行数检查(
SQL%ROWCOUNT)、简单存在性判断 - 不适合隐式游标:需要逐行处理、字段映射复杂、要跳过某些行、需异常分支控制
- 注意
SQL%NOTFOUND在隐式游标中只反映最后一次 DML 或SELECT INTO结果,不能当循环条件用
显式游标 FOR 循环 vs 手动 OPEN/FETCH/CLOSE
FOR rec IN cursor_name LOOP 是最常用也最安全的写法,Oracle 自动管理打开、取值、关闭,且底层做了批量提取(array fetch)优化,默认一次取 15 行(受 PGA_AGGREGATE_TARGET 影响)。手动控制 OPEN/FETCH/CLOSE 只在两种情况必要:需要动态 SQL、或必须精确控制 fetch 数量(比如流式导出防内存溢出)。
手动写容易漏 CLOSE,尤其在异常路径里——没关的游标会持续占用 session 的打开游标数(受 OPEN_CURSORS 限制),最终报 ORA-01000: maximum open cursors exceeded。
- 优先用
FOR循环:代码简洁、不易出错、性能不输手动 - 手动方式仅当需要
FETCH ... BULK COLLECT INTO控制批次大小,或游标定义依赖运行时变量 - 若手动,务必在
EXCEPTION块里加IF cursor_name%ISOPEN THEN CLOSE cursor_name;
BULK COLLECT 的内存和性能临界点
BULK COLLECT INTO 能显著减少上下文切换,但把几千行数据一次性灌进 PL/SQL 集合,会吃掉大量 PGA 内存。当集合元素含长字符串、LOB 或嵌套对象时,很容易触发 ORA-04030: out of process memory,尤其在共享服务器模式或 PGA 限制严的库上。
默认无 LIMIT 的 BULK COLLECT 相当于“全量加载”,和不用 BULK 没本质区别——只是把慢放到了第一次 FETCH。真正平衡点通常在 100–500 行之间,具体看单行平均大小和并发压力。
- 必须带
LIMIT n,比如FETCH c1 BULK COLLECT INTO t1 LIMIT 100 - 避免对大字段(如
CLOB)做BULK COLLECT,改用游标变量 + 应用层分块读 -
EXTEND和TRIM操作本身有开销,别在循环里反复调用
游标变量(REF CURSOR)和静态游标的适用边界
静态游标编译期绑定 SQL,能走执行计划缓存,速度快、可被 SQL Tuning Advisor 分析;游标变量(SYS_REFCURSOR)是运行时才绑定,无法预编译,执行计划可能不稳定,还容易被误当成“更灵活”而滥用。
常见误用:把本该用静态游标的地方改成游标变量,只为图个“复用变量名”。结果是硬解析增多、无法利用游标共享池、调试时看不到真实 SQL 文本。
- 用静态游标:SQL 固定、需性能敏感、要配合
%ROWTYPE或记录类型 - 用游标变量:必须把结果集传给调用方(如 Java 的
CallableStatement)、或 SQL 动态拼接无法避免 - 游标变量赋值后,原游标变量内容不可再读(类似指针转移),别指望多次
FETCH











