SQL%ROWCOUNT动态反映上一条DML实际影响行数,须紧随INSERT/UPDATE/DELETE后立即读取;FORALL返回总成功行数,失败项不计入;跨块需赋值局部变量保存。
SQL%ROWCOUNT 在 DML 后立刻读取才有效
这个属性不是“快照”,而是动态反映上一条可执行语句影响的行数。一旦中间插入了 select、open 游标、甚至另一个 insert,它的值就立刻被覆盖。
常见错误现象:SQL%ROWCOUNT 返回 0 或意外值,其实只是因为读取时机不对。
- 必须紧跟在
INSERT/UPDATE/DELETE之后,中间不能有任何其他可执行语句 - 在
FORALL中,它返回的是整个批量操作影响的总行数,不是单次迭代的 - 如果 DML 语句没执行(比如被
IF跳过),SQL%ROWCOUNT保持上一次值,不会自动清零
UPDATE/DELETE 没匹配到行时 SQL%ROWCOUNT 是 0
这和很多人直觉相反——以为“执行了语句就有值”。实际上,它统计的是**实际被修改或删除的行数**,不是“语句是否运行”。
使用场景:判断更新是否生效,避免误认为数据已变更。
-
UPDATE emp SET sal = sal * 1.1 WHERE empno = 9999;—— 若无此员工,SQL%ROWCOUNT就是0 - 别用
SQL%FOUND替代,它只在游标或FETCH后有意义;DML 后只能靠SQL%ROWCOUNT - 注意空字符串、
NULL条件可能意外导致无匹配,让SQL%ROWCOUNT为0
PL/SQL 中嵌套块会影响 SQL%ROWCOUNT 的作用域
每个 PL/SQL 块都有独立的 SQL%ROWCOUNT 上下文。外层块看不到内层 DML 的结果,除非显式传递。
性能影响:没有额外开销,但逻辑错位会导致业务判断失效,比如日志记录行数错误、补偿逻辑漏触发。
- 内层
BEGIN...END;里的DELETE FROM log_table;不会改变外层的SQL%ROWCOUNT - 若需跨块使用,应立即将值赋给局部变量,如
l_affected := SQL%ROWCOUNT; - 函数内部执行 DML 后返回
SQL%ROWCOUNT是安全的,但调用方仍要立刻读取,不能隔语句再取
批量操作 FORALL + SAVE EXCEPTIONS 下 SQL%ROWCOUNT 的特殊行为
这是最容易踩坑的地方:FORALL 默认不抛异常,但 SQL%ROWCOUNT 依然只反映成功执行的行数,不包含被跳过的失败项。
兼容性注意:Oracle 10g 及以后支持,但 9i 不支持 SAVE EXCEPTIONS,强行使用会报 PLS-00306 错误。
-
FORALL i IN 1..emp_ids.COUNT SAVE EXCEPTIONS UPDATE emp SET ...—— 即使部分失败,SQL%ROWCOUNT仍是实际更新的行数 - 想获知失败数量?得查
SQL%BULK_EXCEPTIONS.COUNT,而不是反推emp_ids.COUNT - SQL%ROWCOUNT - 如果用了
INDICES OF或VALUES OF,SQL%ROWCOUNT仍统计物理影响行数,不是绑定数组长度
真正麻烦的是混合场景:带条件的 FORALL、嵌套循环里穿插单条 DML、或者用 EXECUTE IMMEDIATE 动态执行。这时候 SQL%ROWCOUNT 的归属极易混淆,建议凡涉及关键计数,一律用局部变量承接并注释清楚来源。










