是,但仅限隐式声明的游标;for rec in cursor_name loop 或 for rec in (select...) 会自动关闭,而 sys_refcursor 变量不支持该语法,须显式 open/fetch/close。
pl/sql 中 for 循环游标会自动关闭吗?
会,但仅限于隐式声明的游标。只要用 for rec in cursor_name loop 或 for rec in (select ...) 这种写法,pl/sql 就在循环结束时自动关闭游标、释放上下文——你不用(也不该)手动调用 close。
容易踩的坑是误以为“所有游标都自动关”,结果在显式游标上套用 FOR 循环语法,比如:
DECLARE
CURSOR c IS SELECT * FROM emp;
BEGIN
FOR r IN c LOOP -- ✅ 合法:c 是已声明的显式游标名
NULL;
END LOOP; -- ✅ 循环结束后 c 自动 CLOSE
END;
但下面这种就错:
DECLARE
c SYS_REFCURSOR;
BEGIN
OPEN c FOR SELECT * FROM emp;
FOR r IN c LOOP -- ❌ 编译报错:PLS-00221: 'C' is not a cursor
NULL;
END LOOP;
END;
-
FOR循环只接受游标名或查询语句,不接受SYS_REFCURSOR变量 - 显式声明的游标(
CURSOR c IS ...)可以用于FOR循环,且自动关闭 - 动态打开的
SYS_REFCURSOR必须用FETCH+LOOP+ 显式CLOSE
什么时候必须自己 OPEN/FETCH/CLOSE?
当你需要控制取数节奏、做条件中断、复用同一游标多次遍历,或者游标来源是动态 SQL 时,FOR 循环就不够用了。
典型场景包括:
- 查到某条记录就退出,不继续遍历全表(
EXIT WHEN配合FETCH) - 把游标传给另一个过程(只能传
SYS_REFCURSOR,不能传FOR循环里的隐式游标) - 批量处理时分批
FETCH BULK COLLECT,避免内存撑爆 - 需要检查
%NOTFOUND状态做逻辑分支(FOR循环里拿不到这个属性)
示例中漏掉 CLOSE 是高频错误:
DECLARE
c SYS_REFCURSOR;
r emp%ROWTYPE;
BEGIN
OPEN c FOR SELECT * FROM emp WHERE ROWNUM <h3>
<code>FOR</code> 循环里能用 <code>EXIT</code> 或 <code>CONTINUE</code> 吗?</h3><p>能,但要注意它们只跳出当前循环体,不影响游标的自动关闭行为——无论怎么 <code>EXIT</code>,循环结束时仍会自动 <code>CLOSE</code>。</p><p>常见误操作是以为 <code>EXIT</code> 会跳过清理:</p><pre class="brush:php;toolbar:false;">FOR r IN (SELECT * FROM emp) LOOP
IF r.sal > 5000 THEN
INSERT INTO high_sal_log VALUES (r.empno, SYSDATE);
EXIT; -- ✅ 跳出循环,但游标依然自动关闭
END IF;
END LOOP;
-
EXIT和CONTINUE在FOR循环中完全合法,Oracle 11g+ 支持 - 不要在循环里加
CLOSE—— 编译会报错:PLS-00378: illegal use of cursor - 如果循环里发生异常且未捕获,游标仍会自动关闭(这是 Oracle 的保证)
性能和兼容性:隐式游标 vs 显式游标 FOR 循环
两者底层都是隐式游标(Oracle 称为 “cursor for loop cursor”),所以执行计划、绑定变量处理、软解析行为几乎一致。差异主要在可维护性和调试便利性上。
- 子查询形式(
FOR r IN (SELECT ...))更简洁,适合一次性逻辑,但 SQL 无法复用或单独 explain - 先声明再用(
CURSOR c IS SELECT ...; FOR r IN c LOOP)便于在包中集中管理 SQL,也方便用DBMS_METADATA.GET_DDL查看定义 - Oracle 12c+ 对两种写法都做了优化,比如自动启用
BULK COLLECT内部缓冲,但如果你显式写BULK COLLECT,反而可能绕过这些优化 - 注意:
FOR循环不支持UPDATE ... WHERE CURRENT OF,要更新当前行必须用显式游标 +FOR UPDATE
最常被忽略的一点:游标自动关闭只发生在 PL/SQL 引擎层面,如果游标被赋值给包级变量或通过 OUT 参数传出,那它就脱离了 FOR 循环的作用域,自动关闭机制不再适用——这时候你得自己管好开和关。










