oracle 19c存储过程异常处理必须用exception块捕获具体错误,配合sqlcode/sqlerrm获取上下文;预定义异常(如no_data_found)仅在特定场景触发;非预定义异常需用pragma exception_init绑定错误号;when others不可仅打日志,须raise_application_error或自治事务写日志;批量处理需自治事务日志+断点记录。

直接说结论:Oracle 19c 存储过程中处理异常,必须用 EXCEPTION 块捕获具体错误,配合 SQLCODE 和 SQLERRM 获取上下文,且 WHEN OTHERS 后不能只打日志——得 RAISE_APPLICATION_ERROR 或写入自治事务日志表,否则错误被吞、调用方收不到失败信号。
预定义异常怎么用?别漏掉常见错误码
Oracle 19c 内置了 21 个预定义异常(如 NO_DATA_FOUND、TOO_MANY_ROWS、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 不走这个 - 除零错误
ZERO_DIVIDE是 PL/SQL 层面的,不是 SQL 层报的ORA-01476,两者要分清
非预定义异常怎么绑定错误号?pragma exception_init 是关键
很多 Oracle 错误(比如 ORA-00942 表不存在、ORA-02291 外键约束失败)没有预定义常量,必须手动绑定:
- 声明一个
EXCEPTION变量,再用PRAGMA EXCEPTION_INIT关联错误号,例如:my_table_missing EXCEPTION; PRAGMA EXCEPTION_INIT(my_table_missing, -942); - 错误号必须带负号,且是 Oracle 实际报出的
ORA-后数字,不能写成942或ORA-00942 - 绑定后,在
EXCEPTION块里用WHEN my_table_missing THEN单独处理,比WHEN OTHERS更精准 - 注意:
PRAGMA EXCEPTION_INIT必须放在声明区(IS/AS后),不能放在BEGIN之后
WHEN OTHERS 怎么写才不埋雷?必须中断流程或重抛
WHEN OTHERS 是兜底,但滥用会导致错误静默、调用方无法感知失败:
- 禁止只写
DBMS_OUTPUT.PUT_LINE(SQLERRM)—— 客户端根本收不到任何错误信号,事务也不回滚 - 正确做法是:先记录日志(用自治事务写入日志表),再调用
RAISE_APPLICATION_ERROR(-20001, 'xxx')中断执行 - 如果业务允许忽略某些错误(比如
ORA-00955对象已存在),得在WHEN OTHERS里判断SQLCODE,而不是无条件吞掉 -
SQLCODE返回的是数值(如 -1),SQLERRM返回带ORA-前缀的字符串,两者要配合用,不能只取一个
批量处理中异常怎么不影响已提交批次?自治事务 + 断点记录是刚需
用 BULK COLLECT 处理百万级数据时,某一批出错不能让前面已 COMMIT 的批次失效,也不能丢掉错误现场:
- 日志写入必须加
PRAGMA AUTONOMOUS_TRANSACTION,否则主事务一回滚,日志也消失 - 每批开始前,把当前处理的
ROWID或时间戳写入控制表,失败后可定位断点续跑 -
EXCEPTION块里不能只ROLLBACK—— 已COMMIT的批次无法回滚,只能记录失败并RAISE_APPLICATION_ERROR终止后续批次 - 别依赖外部重试机制,存储过程自己得判断剩余数据是否还有,用
GET DIAGNOSTICS row_count = ROW_COUNT或游标%FOUND状态
最易被忽略的点:异常处理不是加个 WHEN OTHERS 就完事。19c 对事务边界和自治事务更敏感,日志没进表、错误没重抛、批次没断点记录——线上跑着跑着就“成功”了,其实数据只处理了一半。











