游标声明后%ISOPEN恒为FALSE;%ROWCOUNT仅在FETCH后递增且不表示总行数;隐式游标SQL%ROWCOUNT可靠,REF CURSOR的%ROWCOUNT不可用于业务判断;循环退出应依赖%NOTFOUND而非%ROWCOUNT。
游标刚声明完,%ISOPEN 一定是 FALSE
pl/sql 中游标变量刚被声明(declare cursor c is ...)时,它还没被打开,c%isopen 必然为 false。很多人误以为“声明即存在”,就直接在 open 前查 %isopen 来做逻辑分支,结果永远走不到预期分支。
实操建议:
- 只在
OPEN之后、CLOSE之前检查%ISOPEN,比如异常处理块里判断是否需要补关 - 不要用
%ISOPEN控制主流程是否打开游标——该OPEN就OPEN,别绕弯 - 显式游标(非 REF CURSOR)无法在未
OPEN时访问%ROWCOUNT,会报ORA-01001: invalid cursor
%ROWCOUNT 在 FETCH 后才开始累积,且不反映总行数
%ROWCOUNT 是“已成功 FETCH 的次数”,不是“当前结果集总行数”,也不是“剩余待取行数”。刚 OPEN 但没 FETCH,它的值是 0;FETCH 一次后变 1,再 FETCH 成功变 2……直到最后一次 FETCH 返回 NO_DATA_FOUND,%ROWCOUNT 保持上一次的值不变。
常见错误现象:
- 用
c%ROWCOUNT = 0判断游标是否“无数据”——错!必须等FETCH后看%NOTFOUND或捕获异常 - 在循环开头检查
c%ROWCOUNT = 0就退出,导致第一行被跳过 - 期望
%ROWCOUNT能返回SELECT COUNT(*)那样的总数——它做不到,游标不预读全量结果
REF CURSOR 和隐式游标,%ROWCOUNT 行为完全不同
隐式游标(如 SELECT ... INTO)的 SQL%ROWCOUNT 在执行后立即可读,且能准确反映影响/查询到的行数;而 REF CURSOR 是动态的,rc%ROWCOUNT 只有在绑定并 FETCH 后才有意义,且受底层 SQL 执行路径影响(比如带 ROWNUM 截断或并行查询时,可能比实际匹配行少)。
使用场景差异:
- 做单行赋值(
SELECT ... INTO)?优先信SQL%ROWCOUNT,它稳定、即时 - 用
REF CURSOR返回结果集给调用方?别依赖%ROWCOUNT做业务判断,它只是“已取几行”的快照,不是元数据 - 想知总数?要么额外跑
COUNT(*),要么在打开游标前用SELECT COUNT(*) FROM (...) t预估——别指望%ROWCOUNT
循环中混用 %NOTFOUND 和 %ROWCOUNT 容易漏数据
典型错误写法:LOOP FETCH c INTO ...; EXIT WHEN c%ROWCOUNT = 0; —— 这根本不会退出,因为第一次 FETCH 成功后 %ROWCOUNT 就是 1 了。正确退出条件只能是 c%NOTFOUND(表示上次 FETCH 没取到)或 c%FOUND(表示取到了)。
关键细节:
-
%NOTFOUND在FETCH未命中时为TRUE,但首次FETCH前它是NULL,不能直接用于WHILE条件判断 - 标准安全写法是:先
FETCH,再在循环末尾EXIT WHEN c%NOTFOUND - 如果循环体里有
CONTINUE或异常跳转,要小心%ROWCOUNT和%NOTFOUND状态是否被覆盖或延迟更新
真正容易被忽略的是:游标状态不是原子的。FETCH 执行瞬间,%ROWCOUNT 加 1,%NOTFOUND 变 FALSE;下一次 FETCH 失败时,%ROWCOUNT 不变,%NOTFOUND 才变 TRUE。这两个值不同步,也不能互相推导。










