Oracle 19c存储过程异常处理必须用EXCEPTION块显式捕获,WHEN OTHERS后须RAISE_APPLICATION_ERROR或自治事务写日志,否则错误被吞;预定义异常仅在特定语法(如SELECT INTO)触发,非预定义异常需PRAGMA EXCEPTION_INIT绑定错误号,定位真错误行须用DBMS_UTILITY.FORMAT_ERROR_BACKTRACE()。

Oracle 19c 存储过程中捕获异常,必须用 EXCEPTION 块显式包裹,且不能只靠 WHEN OTHERS THEN 打日志完事——错误会被静默吞掉,调用方收不到失败信号,事务状态也不可控。
预定义异常要按实际触发条件用,别硬套
Oracle 内置的预定义异常(如 NO_DATA_FOUND、DUP_VAL_ON_INDEX)只在特定语法路径下才抛出,不是所有相似场景都生效:
-
NO_DATA_FOUND仅在SELECT INTO没查到数据时触发;用FETCH或BULK COLLECT不会抛这个 -
TOO_MANY_ROWS只对SELECT INTO生效;游标循环或FOR UPDATE即使返回多行也不触发 -
DUP_VAL_ON_INDEX仅对应主键/唯一索引冲突;违反CHECK、NOT NULL或外键约束(ORA-02291)不走这个
非预定义异常要用 PRAGMA EXCEPTION_INIT 绑定错误号
遇到像外键违例(ORA-02291)、检查约束失败(ORA-02290)这类没内置名字的错误,得手动关联:
- 先声明自定义异常变量:
fk_violation EXCEPTION; - 再用
PRAGMA EXCEPTION_INIT(fk_violation, -2291);把它绑到错误号 - 之后就能在
EXCEPTION块里写WHEN fk_violation THEN ...做针对性处理 - 注意:错误号带负号,
-2291不能写成2291,否则绑定失败
定位真实错误位置得靠 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE()
SQLCODE 和 SQLERRM 只告诉你“在哪个 EXCEPTION 块被捕获”,但真正的出错行号藏在堆栈里:
- 必须在
EXCEPTION块开头立即调用:DBMS_UTILITY.FORMAT_ERROR_BACKTRACE() - 返回类似
ORA-06512: at "SCOTT.PROC_A", line 42的字符串,这才是真实 PL/SQL 行号 - 如果过程里用了
EXECUTE IMMEDIATE,堆栈会断在动态语句那行,得去查动态 SQL 内容本身 - 别用
SQLERRM直接截取——它带前缀(如"ORA-01403: no data found"),要纯消息得传参:SQLERRM(-1403)
WHEN OTHERS 后必须 RAISE_APPLICATION_ERROR 或自治事务写日志
裸写 WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(...) 是危险操作:
- 错误被吞,上层调用者以为成功,事务可能已部分提交
- 正确做法二选一:
— 用RAISE_APPLICATION_ERROR(-20001, '业务异常: ' || SQLERRM)重新抛出,让调用方感知失败
— 或用自治事务插入日志表(需提前加PRAGMA AUTONOMOUS_TRANSACTION),再COMMIT确保日志落盘 - 批量处理时尤其关键:单条失败不能阻塞整个批次,但必须记录断点和错误详情,否则重跑无依据
真正难的不是写 EXCEPTION 块,而是判断该不该吞、在哪一层吞、吞了之后怎么让下游知道“表面成功,实际有坑”。行号不准、错误号没绑定、日志没落盘——这三个点踩中任意一个,排查成本就翻倍。


















