必须用SQL%ROWCOUNT且在COMMIT前读取才能获取DML真实影响行数;显式游标%ROWCOUNT仅统计FETCH次数,不能用于DML计数;SQL%FOUND/SQL%NOTFOUND需配合判断语义。

不能直接用显式游标的 %ROWCOUNT 获取 DML 影响行数;必须用隐式游标 SQL%ROWCOUNT,且必须在 COMMIT 前读取。
SQL%ROWCOUNT 是唯一可靠的批量更新计数方式
显式游标(比如 CURSOR c IS SELECT ...)的 %ROWCOUNT 返回的是已成功 FETCH 的行数,不是 DML 操作影响的行数。想监控 UPDATE/DELETE/INSERT 实际改了几行,只能依赖 Oracle 自动创建的隐式游标 SQL 及其属性。
关键点:
-
SQL%ROWCOUNT在 DML 语句执行完后立即生效,值为本次操作真实影响的行数 - 该值在
COMMIT或ROLLBACK后被清零,后续再读就是 0 - 即使 DML 语句没匹配到任何行,
SQL%ROWCOUNT也会返回 0,不会报错
常见错误:把 SQL%ROWCOUNT 放在 COMMIT 后面
这是生产环境最常踩的坑。例如:
BEGIN
UPDATE emp_new SET sal = sal + 100 WHERE deptno = 10;
COMMIT;
DBMS_OUTPUT.PUT_LINE('影响了 ' || SQL%ROWCOUNT || ' 行'); -- ❌ 这里永远输出 0
END;原因:隐式游标在 DML 执行结束时就关闭了,COMMIT 不是游标生命周期的一部分,它只是事务控制。一旦 COMMIT 完成,SQL 上下文已被释放。
正确写法必须是:
BEGIN
UPDATE emp_new SET sal = sal + 100 WHERE deptno = 10;
DBMS_OUTPUT.PUT_LINE('影响了 ' || SQL%ROWCOUNT || ' 行'); -- ✅ 必须在这行
COMMIT;
END;SQL%FOUND 和 SQL%NOTFOUND 要配合使用才安全
单靠 SQL%ROWCOUNT 能知道数字,但无法区分“影响 0 行”是条件不匹配,还是表为空,或是逻辑 bug。这时候要结合布尔属性判断语义:
-
SQL%FOUND为TRUE⇨ 至少影响 1 行(此时SQL%ROWCOUNT >= 1) -
SQL%NOTFOUND为TRUE⇨ 影响 0 行(此时SQL%ROWCOUNT = 0) - 二者互斥,不会同时为
TRUE,但可以都为FALSE(极罕见,如某些自治事务场景)
典型防御性写法:
BEGIN
DELETE FROM log_table WHERE created_date < SYSDATE - 30;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('无过期日志可清理');
ELSE
DBMS_OUTPUT.PUT_LINE('已清理 ' || SQL%ROWCOUNT || ' 条日志');
END IF;
COMMIT;
END;注意:不要混淆显式游标 %ROWCOUNT 和 SQL%ROWCOUNT
如果你写了显式游标并用来驱动循环更新(比如逐条 UPDATE),那有两个 %ROWCOUNT 在共存:
-
c_emp%ROWCOUNT:表示从游标c_emp中 fetch 了多少次(即处理了几条源数据) -
SQL%ROWCOUNT:表示最近一次UPDATE语句实际改了几行(可能每次都是 1,也可能因 WHERE 条件失效变成 0)
二者数值通常不同,且用途完全不同。监控“批量更新效果”,只看 SQL%ROWCOUNT;监控“游标遍历进度”,才看 c_emp%ROWCOUNT。混用会导致日志误导或告警误触发。


















