SQL%FOUND和SQL%NOTFOUND判断上一条DML或SELECT INTO是否影响至少一行;显式游标须用c1%FOUND/c1%NOTFOUND,且循环中应FETCH后立即EXIT WHEN c1%NOTFOUND。
SQL%FOUND 和 SQL%NOTFOUND 到底在判断什么
它们不是在判断“游标有没有打开”或“结果集是否为空”,而是在判断**上一条 dml 或 select into 语句是否影响了至少一行数据**。也就是说,sql%found 和 sql%notfound 是隐式游标的属性,跟显式声明的 declare cursor ... 游标无关——除非你用的是 select into 这种单行查询。
常见错误现象:
– 显式游标循环中直接写 IF SQL%FOUND THEN ...,结果永远为 FALSE(因为 FETCH 不改变隐式游标状态);
– 在 OPEN c1; FETCH c1 INTO ...; 后立刻查 SQL%FOUND,但此时它反映的是前一条非游标语句(比如前面的 UPDATE)的结果。
-
SQL%FOUND为TRUE当且仅当上一条INSERT/UPDATE/DELETE或SELECT INTO影响了 ≥1 行 -
SQL%NOTFOUND是SQL%FOUND的逻辑反,但注意:对SELECT INTO,没查到会抛NO_DATA_FOUND异常,根本走不到判断这步 - 显式游标要用自己的属性,比如
c1%FOUND、c1%NOTFOUND,别混用SQL%前缀
显式游标该用 c1%FOUND 还是 c1%NOTFOUND 做循环退出条件
用 c1%NOTFOUND 更安全。因为 FETCH 每次只取一行,取完后游标位置停在“下一行尚未读取”的状态;只有当 FETCH 尝试读取却无数据可读时,c1%NOTFOUND 才变为 TRUE。
常见错误现象:
– 写成 EXIT WHEN c1%FOUND,导致只处理第一行就退出;
– 在 FETCH 前就检查 c1%NOTFOUND,此时恒为 FALSE(游标刚打开,还没取过数据)。
- 标准写法是:先
FETCH c1 INTO ...,再立即EXIT WHEN c1%NOTFOUND - 不要在
FETCH前判断,也不要把它放在循环条件里(比如WHILE NOT c1%NOTFOUND LOOP),容易漏最后一行 -
c1%FOUND可用于确认本次FETCH是否成功取到数据,但不建议作为主循环退出依据
SELECT INTO + SQL%NOTFOUND 能不能替代显式游标
能,但只适用于**预期最多一行**的场景。一旦可能返回多行,SELECT INTO 会直接报 TOO_MANY_ROWS 错误,程序中断。
使用场景:
– 验证主键是否存在(SELECT id INTO v_id FROM t WHERE pk = ?);
– 查询配置项、单条状态记录等确定性单行结果。
- 如果
SELECT INTO没查到,会触发NO_DATA_FOUND异常,SQL%NOTFOUND根本不会被执行到 - 想安全捕获“没查到”,必须用
EXCEPTION WHEN NO_DATA_FOUND THEN ...,而不是依赖SQL%NOTFOUND - 性能上,
SELECT INTO比显式游标开销略小(省去OPEN/CLOSE),但差别微乎其微,别为此牺牲可读性
PL/SQL 中游标属性的兼容性陷阱
不同 Oracle 版本对游标属性行为基本一致,但有两处容易被忽略:
-
%ROWCOUNT在OPEN后为 0,FETCH第一次后才变成 1;但SQL%ROWCOUNT在SELECT INTO前是未定义的,执行后才是 1 或报错 - 动态 SQL(
EXECUTE IMMEDIATE)执行SELECT INTO后,SQL%FOUND可用;但执行OPEN ... FOR动态查询后,得到的是显式游标,得用cur%FOUND,不是SQL% - 包内游标变量(
SYS_REFCURSOR)没有%FOUND属性,只能靠FETCH后的SQL%ROWCOUNT或额外计数器判断是否还有数据
最常被绕过的点:把显式游标和隐式游标的状态混在一起理解,以为 SQL% 系列能反映任意游标动作——其实它只忠于最后那条非游标 DML 或 SELECT INTO 语句。










