显式游标 fetch 后 %notfound 为 true 表示活动集为空或已遍历完毕,属正常行为;它不同于 no_data_found 异常,后者仅在 select into 中抛出,而显式游标需主动检查 %notfound。

显式游标 fetch 后 %NOTFOUND 为 true 的原因
显式游标没数据,最常见也最直接的原因是 FETCH 执行后游标属性 %NOTFOUND 返回 TRUE —— 这不是错误,而是正常行为,表示活动集为空或已遍历完毕。它和 NO_DATA_FOUND 异常有本质区别:NO_DATA_FOUND 只在 SELECT INTO(隐式游标场景)中抛出;而显式游标从不抛这个异常,只靠 %NOTFOUND 主动判断。
-
SELECT语句本身没查到任何行(比如WHERE deptno = 99确实无匹配部门) - 游标声明时用了
FOR UPDATE,但目标行被其他会话锁住且不可见(尤其在READ COMMITTED隔离级别下) - 查询条件中用了未初始化的变量(如
v_dept_id为NULL),导致WHERE deptno = v_dept_id永远不成立(NULL = anything恒为FALSE) - 游标定义里漏写了
WHERE条件,或逻辑写反(例如写成AND status != 'ACTIVE'却期望拿到活跃数据)
为什么 OPEN 不报错,但 FETCH 拿不到数据
OPEN 只做语法校验和执行计划准备,不真正读取数据;真正触发查询并填充活动集的是 FETCH。所以即使表名拼错、列不存在,OPEN 也可能成功(只要语句能解析),但首次 FETCH 就会报 ORA-00904 或 ORA-00942。反过来,如果 OPEN 成功、FETCH 后 %NOTFOUND 为 TRUE,说明语句合法、执行了、结果就是空——这时该检查业务逻辑或数据现状,而不是怀疑语法。
- 确认
SELECT语句单独在 SQL*Plus 或 SQL Developer 中执行是否返回行 - 检查绑定变量值:用
DBMS_OUTPUT.PUT_LINE('dept_id=' || v_dept_id);输出实际传参 - 避免在
WHERE中直接比较NULL,改用IS NULL或NVL - 若游标含
FOR UPDATE,加WAIT 3或改用SKIP LOCKED(12c+)观察是否被阻塞
FETCH 后忘记检查 %NOTFOUND 导致逻辑跳过
很多开发者写完 FETCH 就直接处理变量,没做存在性判断。结果游标为空时,变量仍保持声明时的初始值(如 NUMBER 是 NULL,VARCHAR2 是空字符串),后续逻辑误把“无数据”当作“有效默认值”处理。
- 必须在每次
FETCH后立刻检查cursor_name%NOTFOUND,不能依赖变量是否为NULL - 推荐用
EXIT WHEN cursor_name%NOTFOUND;放在FETCH后紧邻位置,避免遗漏 - 不要在
LOOP外部提前赋值变量(如v_name := 'N/A';),这会掩盖空结果的真实含义 - 若需区分“查不到”和“查到但字段为 NULL”,应在
SELECT中用NVL(col, 'MISSING')显式标记
使用 CURSOR FOR LOOP 时的隐含行为
CURSOR FOR LOOP 看似省事,但它内部自动做了 OPEN、FETCH、%NOTFOUND 判断和 CLOSE。如果循环体一次都没执行,不代表出错,只代表游标活动集为空。这时候想做“无数据时的兜底处理”,不能放在循环内,而要靠外部逻辑补位。
-
CURSOR FOR LOOP体内代码完全不会执行,所以别在里面放初始化或计数逻辑 - 需要“无数据则插入默认行”这类操作时,得先用普通显式游标 +
%ROWCOUNT判断,或改用SELECT COUNT(*)预检 -
%ROWCOUNT在CURSOR FOR LOOP中始终为 0(因为每次迭代都是新FETCH),不能用来判断总行数 - 若循环中需修改数据(
UPDATE ... WHERE CURRENT OF),必须在游标定义里显式加FOR UPDATE,否则报ORA-01001
%NOTFOUND 当成可选检查,或者混淆它和 NO_DATA_FOUND 的触发边界。











