循环内OPEN/CLOSE游标会触发硬解析、共享池查找、UGA内存分配及latch争用等开销;REF CURSOR动态拼接更导致无计划复用和ORA-01000风险;应使用外层定义的参数化静态游标。
频繁在循环内打开和关闭游标,本质是在反复申请和释放数据库会话级资源——这不是“多写几行代码”的小事,而是直接触发 oracle 的游标句柄分配、sql 解析、执行计划缓存查找、内存页分配等开销,性能损耗远超直观预期。
循环内 OPEN/CLOSE 游标会触发哪些实际开销?
每次 OPEN 都不是轻量操作:
- Oracle 必须校验 SQL 语法与语义(即使同一语句),重新做绑定变量检查
- 查询共享池(Shared Pool)中是否存在可复用的执行计划;若无,则硬解析——CPU 和 latch 争用显著上升
- 为该游标分配私有内存(UGA 中的游标上下文),并注册到会话的游标数组中
-
CLOSE不等于“立刻释放”:它只是标记为可重用,真实回收依赖 LRU 算法,且受open_cursors参数限制
常见错误写法:参数化游标被误放在循环里声明或打开
比如想根据 dept_id 逐个查员工,却写成:
FOR i IN 1..10 LOOP
DECLARE
CURSOR c_emp IS SELECT ename FROM emp WHERE deptno = i;
BEGIN
FOR r IN c_emp LOOP ... END LOOP; -- 每次都新建游标定义 + OPEN/FETCH/CLOSE
END;
END LOOP;这比把游标提到外层、只传参调用 FOR r IN c_emp(i) LOOP 慢 3–5 倍(实测 10 万次迭代下 CPU 时间差异明显)。更糟的是,它绕过了 Oracle 对静态游标的编译期优化,所有解析都变成运行时硬解析。
REF CURSOR 在循环里 OPEN 更危险
REF CURSOR 本身是运行时句柄,OPEN cur FOR 'SELECT ...' || v_sql 每次都走完整动态 SQL 流程:
- 字符串拼接 → 绑定变量注入风险(若未用 USING)
- 无共享池复用可能(即使 SQL 文本相同,因字符串拼接导致哈希值不同)
- 未
CLOSE时句柄持续累积,达到open_cursors限额后直接报ORA-01000: maximum open cursors exceeded
尤其在包级 REF CURSOR 变量中长期持有却不 CLOSE,极易引发会话级资源泄漏。
真正省资源的写法:静态游标 + 参数 + 外层定义
只要业务逻辑允许,优先用这一种:
CURSOR c_emp(p_deptno NUMBER) IS SELECT ename, sal FROM emp WHERE deptno = p_deptno; <p>BEGIN FOR dept IN (SELECT deptno FROM dept) LOOP FOR r IN c_emp(dept.deptno) LOOP -- 单次 OPEN,自动 FETCH/CLOSE ... END LOOP; END LOOP; END;
注意:c_emp 是静态定义,编译期已知结构;FOR r IN c_emp(...) 是唯一安全支持参数的隐式游标用法;整个过程只产生 1 个游标句柄(复用),且执行计划可缓存复用。
最易被忽略的一点:游标资源消耗不只看“是否 CLOSE”,而要看“是否真正释放”——静态游标在 FOR 循环结束时自动 CLOSE 是可靠的,但 REF CURSOR 必须显式 CLOSE,且不能依赖异常时的自动清理。一旦漏掉,下次调用可能直接失败。


















