pl/sql显式游标未close就退出会导致游标资源泄漏,引发ora-01000错误;必须在exception中用%isopen检查后close,且ref cursor的关闭责任在调用方。
pl/sql游标没close就退出会怎样
会泄漏游标资源,严重时触发 ora-01000: maximum open cursors exceeded。oracle对每个会话的打开游标数有限制(默认300,可调但有代价),泄漏积累后整个会话甚至数据库连接都会卡住。
常见错误现象:程序运行一段时间后突然报错、查询变慢、DBA告警游标数飙升;而代码里明明只开了几个游标——大概率是异常路径漏了 CLOSE。
- 显式游标(
DECLARE CURSOR ...)必须手动OPEN/CLOSE,不自动释放 -
FOR rec IN cur LOOP这种隐式循环会自动管理,但仅限该语法块内;一旦自己写OPEN+FETCH,就得自己兜底 - 即使过程正常结束,若中间
RETURN、EXIT或未捕获异常,CLOSE就会被跳过
用EXCEPTION块确保CLOSE执行
最直接可靠的方案:所有 OPEN 后,立刻在 EXCEPTION 块里补 CLOSE。别指望“最后统一关”,因为异常可能发生在任意位置。
使用场景:需要精细控制 fetch 流程(比如分批处理、条件中断)、或游标定义在包级/过程外需复用时。
示例片段:
DECLARE
CURSOR c_emp IS SELECT * FROM employees WHERE dept_id = 10;
r_emp c_emp%ROWTYPE;
BEGIN
OPEN c_emp;
LOOP
FETCH c_emp INTO r_emp;
EXIT WHEN c_emp%NOTFOUND;
-- 处理逻辑...
END LOOP;
CLOSE c_emp; -- 正常路径
EXCEPTION
WHEN OTHERS THEN
IF c_emp%ISOPEN THEN
CLOSE c_emp; -- 异常路径强制关闭
END IF;
RAISE; -- 重新抛出,不吞异常
END;
-
c_emp%ISOPEN必须检查,否则对已关闭或未打开的游标调用CLOSE会报ORA-01001: invalid cursor - 不要在
EXCEPTION里静默吞掉异常(比如只写NULL),否则上游无法感知失败 - 如果游标是
REF CURSOR(尤其返回给调用方的),CLOSE时机由接收方决定,本过程通常不关
用BEGIN-END嵌套隔离游标生命周期
把游标声明、打开、使用、关闭全包在一个独立的 BEGIN...END 块里,配合局部异常处理,能天然限制作用域,降低泄漏风险。
参数差异:适用于一次性使用的游标,不涉及跨块传递;比全局声明更安全,也比全程手动判断 %ISOPEN 更简洁。
性能影响几乎为零,反而因作用域清晰,Oracle更容易优化内存回收。
- 游标变量在块结束时自动置为未打开状态(但底层资源仍需显式
CLOSE) - 必须在同一个块里完成
OPEN和CLOSE,不能开在A块、关在B块 - 若块内发生异常且未被捕获,块退出时不会自动关游标——所以仍要配
EXCEPTION块
REF CURSOR的释放责任归属
如果过程返回 REF CURSOR 给调用方(比如函数返回 SYS_REFCURSOR),那 CLOSE 是调用方的责任,不是被调用方的。
容易踩的坑:在过程末尾自作主张 CLOSE 返回的 REF CURSOR,结果调用方一取数就报 ORA-01001 —— 游标已被关掉。
- 被调用方只需确保游标在返回前是打开的,且不提前
CLOSE - 调用方拿到后,必须自己
FETCH完再CLOSE,或用高级语言(如Java的ResultSet)自动管理 - 若想让被调用方负责清理,改用临时表或集合类型返回数据,而非游标
复杂点在于责任边界模糊时没人关——比如PL/SQL里调另一个PL/SQL,双方都以为对方会关。真要防这个,文档写清楚,或加日志打点确认关闭动作。










