SQL%ROWCOUNT返回最近一次DML或SELECT INTO影响的行数,执行后须立即读取,否则被后续SQL覆盖;SELECT INTO无数据时为0,UPDATE无匹配行为0且不报错,FORALL返回总计数。
SQL%ROWCOUNT 返回什么值?什么时候才可靠
它返回最近一次 dml(insert、update、delete)或 select into 语句影响的行数,但注意:这个值在语句执行后立刻读取才有效,一旦中间穿插了另一条 sql,就会被覆盖。
-
SELECT INTO找到 0 行 →SQL%ROWCOUNT是 0;找到 1 行 → 是 1;找不到会报NO_DATA_FOUND异常,此时读SQL%ROWCOUNT仍是 0(不是未定义) -
UPDATE没匹配到任何行 →SQL%ROWCOUNT是 0,不会报错,这是常见误判点 - 不要在异常处理块里依赖它——比如
EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT);,这时值已不可信 - 如果用
FORALL批量操作,SQL%ROWCOUNT返回的是总行数,不是单次迭代的
SQL%FOUND 和 SQL%NOTFOUND 到底怎么用才不翻车
这两个是布尔属性,只反映“上一条 SQL 是否成功返回至少一行数据”,但它们的适用范围比很多人想的窄得多。
- 仅对
SELECT INTO、FETCH(显式游标)、UPDATE/DELETE有效;对普通SELECT(没INTO)直接报PLS-00225错误 -
SQL%FOUND在UPDATE影响 0 行时为FALSE,影响 ≥1 行时为TRUE;但它不区分“影响 1 行”和“影响 1000 行” - 别拿它替代
SQL%ROWCOUNT做计数判断,比如写IF SQL%FOUND THEN ... ELSE ... END IF;来判断是否更新成功——逻辑没问题,但掩盖了“到底改了几行”的信息 - 在循环中反复
FETCH显式游标时,SQL%FOUND是游标属性(如cur%FOUND),不是SQL%FOUND;混用会静默失效
隐式游标和显式游标的 %FOUND 行为差异
名字都叫 %FOUND,但隐式游标(即 SQL 语句自动创建的)和显式游标(DECLARE CURSOR ...)的触发时机和语义完全不同。
- 隐式游标
SQL%FOUND只在语句执行完立即生效,且只保留最后一次结果;显式游标cur%FOUND是在每次FETCH后设置,且只要没再FETCH就保持上次值 -
OPEN cur; FETCH cur INTO ...; CLOSE cur;之后再查cur%FOUND,值不变;但SQL%FOUND此时早已被后续任意 SQL 覆盖 - 显式游标支持
%ISOPEN、%ROWCOUNT等更多属性,隐式游标只有%FOUND、%NOTFOUND、%ROWCOUNT、%TOO_MANY_ROWS(仅SELECT INTO) - 一个典型陷阱:
SELECT COUNT(*) INTO v_cnt FROM t WHERE ...;后检查SQL%FOUND——永远是TRUE,因为COUNT(*)总返回一行(哪怕值为 0)
为什么不能用 SQL%FOUND 判断 SELECT 是否有数据
因为 SELECT 本身不触发隐式游标属性更新,除非加 INTO;而加 INTO 又要求必须返回恰好一行,否则出错。所以它根本不是为“查有没有数据”设计的。
- 想安全判断是否存在匹配数据,该用
SELECT COUNT(1) INTO v_cnt FROM ... WHERE ...;然后看v_cnt > 0,而不是碰运气用SQL%FOUND - 若必须用
SELECT INTO,得包EXCEPTION WHEN NO_DATA_FOUND THEN ...,此时SQL%FOUND已无意义 - 用
SELECT 1 INTO dummy FROM ... WHERE ... AND ROWNUM = 1;加异常捕获,比依赖SQL%FOUND更可控 - Oracle 12c+ 可考虑
SELECT ... FETCH FIRST 1 ROW ONLY配合BULK COLLECT,但依然绕不开显式集合判断
真正容易被忽略的是:这些属性不是状态快照,而是执行链上的瞬时标记。一次忘记赋值、多调一次查询、甚至加了个日志 DBMS_OUTPUT.PUT_LINE(里面可能隐含 SQL),都可能让前面读的 SQL%ROWCOUNT 变成废值。










