PL/SQL中EXCEPTION块不生效的主因是作用域错误或异常未显式传播;NO_DATA_FOUND仅由SELECT INTO触发;WHEN OTHERS THEN NULL会隐藏错误;SQLERRM长度限512字节且仅在EXCEPTION内可靠;PRAGMA EXCEPTION_INIT需用负错误号精准捕获特定异常。
PL/SQL里EXCEPTION块不生效的常见原因
不是写了exception就一定能捕获到错误——最常踩的坑是把异常处理写在了错误作用域外,或者用了when others then null却没意识到它会吞掉所有线索。
-
EXCEPTION只能捕获当前BEGIN...END块内抛出的异常;嵌套块里的异常不会自动向上冒泡,除非显式RAISE -
SELECT INTO没查到数据时抛NO_DATA_FOUND,但如果你写成SELECT ... FROM DUAL WHERE 1=0 INTO v_var,它确实会触发,而INSERT/UPDATE/DELETE没匹配行则不会报错(SQL%ROWCOUNT返回0) - 在存储过程里调用另一个过程,被调用者抛异常但没声明
PRAGMA EXCEPTION_INIT或没重抛,调用方的EXCEPTION块收不到
用SQLERRM取错误信息时的三个硬限制
SQLERRM只在EXCEPTION块里可靠,而且它返回的字符串长度上限是512字节——超长的ORA错误描述会被截断,比如某些带长SQL文本的ORA-06512堆栈信息。
- 单独写
DBMS_OUTPUT.PUT_LINE(SQLERRM)可能只打出“ORA-01403: no data found”,但实际错误源头在第8层调用栈,得配合DBMS_UTILITY.FORMAT_ERROR_BACKTRACE -
SQLERRM(-1)能手动构造错误消息,但负数参数必须是合法的Oracle错误号,否则返回空字符串 - 在
WHEN OTHERS THEN里直接用SQLERRM没问题,但在具体异常分支如WHEN NO_DATA_FOUND THEN里再调它,返回的是“no data found”,不是原始错误上下文
什么时候该用PRAGMA EXCEPTION_INIT而不是WHEN OTHERS
靠WHEN OTHERS兜底等于放弃错误分类——你没法区分是权限不足(ORA-01031)、表不存在(ORA-00942)还是死锁(ORA-00060),而这三者恢复策略完全不同。
- 给自定义异常名绑定错误号:
my_privilege_err EXCEPTION; PRAGMA EXCEPTION_INIT(my_privilege_err, -1031);,之后就能写WHEN my_privilege_err THEN ... - 注意负号不能漏:
PRAGMA EXCEPTION_INIT(e, 1031)(无负号)完全无效,Oracle不认正数错误码 - 一个异常变量只能绑定一个错误号,但一个错误号可以被多个变量绑定(虽然没实际意义)
日志记录中SQLERRM和DBMS_UTILITY.FORMAT_ERROR_BACKTRACE怎么搭配
只记SQLERRM就像只记“人摔了”,不记在哪、怎么摔的;FORMAT_ERROR_BACKTRACE才给出函数调用链,但它不包含错误码和主消息,必须合用。
- 典型写法:
EXCEPTION WHEN OTHERS THEN INSERT INTO err_log VALUES ( SYSDATE, SQLCODE, SQLERRM, DBMS_UTILITY.FORMAT_ERROR_BACKTRACE ); -
SQLCODE永远比SQLERRM先取——一旦执行了RAISE或进入EXCEPTION块,SQLCODE就固定了;但若在EXCEPTION里又调了其他可能出错的函数,SQLCODE会被覆盖 -
FORMAT_ERROR_BACKTRACE在10gR2+才稳定,低版本要用DBMS_UTILITY.FORMAT_CALL_STACK凑合,但后者不指向具体行号
PRAGMA——很多团队卡在这一步,最后全扔进WHEN OTHERS里打日志了事。










