必须用pragma exception_init将异常变量与负号错误号(如-1)在declare段绑定,不可漏负号或错位;预定义异常可直接使用,非预定义异常须此方式捕获;捕获后需手动rollback事务并用sqlcode/sqlerrm记录上下文。

直接捕获特定 Oracle 错误,关键在于把 PRAGMA EXCEPTION_INIT 和错误号(带负号)配对写在 DECLARE 段,且不能漏掉负号、不能放错位置。
用 PRAGMA EXCEPTION_INIT 绑定 ORA- 错误号
Oracle 不允许你直接写 WHEN ORA-00001 THEN 这种语法。必须先声明一个异常变量,再用 PRAGMA EXCEPTION_INIT 把它和具体错误号关联起来。这个 pragma 必须出现在 DECLARE 段,且紧挨着异常变量声明之后。
-
PRAGMA EXCEPTION_INIT的第二个参数必须是负整数,比如-1对应ORA-00001,-60对应死锁,-1403对应NO_DATA_FOUND—— 负号漏掉会报PLS-00103 - 同一个异常名不能在同一个块里重复声明,否则报
PLS-00112 - 子程序(如嵌套过程)里要用,得各自重新声明,异常名不跨作用域
示例:
DECLARE
dup_key EXCEPTION;
PRAGMA EXCEPTION_INIT(dup_key, -1); -- ✅ 正确绑定唯一约束冲突
BEGIN
INSERT INTO users(id, name) VALUES (1, 'Alice');
EXCEPTION
WHEN dup_key THEN
DBMS_OUTPUT.PUT_LINE('主键已存在,跳过');
END;
区分预定义异常和非预定义异常的写法
像 NO_DATA_FOUND、TOO_MANY_ROWS、ZERO_DIVIDE 这类 20 多个预定义异常,Oracle 已内置名称,可直接 WHEN NO_DATA_FOUND THEN 使用,无需 PRAGMA。但像 ORA-00942(表不存在)、ORA-01031(权限不足)这类没给名字的,就必须走 PRAGMA 路线。
- 预定义异常名大小写不敏感,但建议全大写保持可读性
-
INVALID_NUMBER(ORA-01722)常在TO_NUMBER()或隐式转换时触发,不是所有字符串转数字失败都抛这个 —— 空字符串、NULL通常不触发,但'abc'会 - 不要依赖
WHEN OTHERS THEN来兜底处理所有业务逻辑错误;它该只用于记录日志 + 重抛,否则掩盖真实问题
捕获后必须显式控制事务状态
Oracle 在抛出异常时不会自动提交或回滚整个事务,但某些错误(如死锁被选为 victim)会由系统自动回滚当前语句级变更,而你的事务仍处于活动状态。这时候如果不在 EXCEPTION 块里手动 ROLLBACK,后续语句可能继续执行并产生脏数据。
- 对
DUP_VAL_ON_INDEX、NO_DATA_FOUND这类不改变数据一致性的错误,可以不ROLLBACK,直接处理逻辑分支 - 对
-60(死锁)、-1(唯一冲突导致插入失败)、-2291(外键约束失败)等涉及 DML 失败的场景,务必在EXCEPTION块内加ROLLBACK - 处理完要重抛吗?要看调用链:如果上层需要知道失败并重试(比如死锁),就
RAISE;如果只是静默忽略(比如去重插入),就不用
获取完整错误上下文:SQLCODE 和 SQLERRM
仅靠异常名不够定位问题,尤其在存储过程中。真正有用的调试信息来自 SQLCODE(返回当前错误号,如 -1)和 SQLERRM(返回完整错误消息,含行号、对象名等)。它们只能在 EXCEPTION 块中安全访问。
-
SQLERRM最长 512 字节,超长会被截断;用SUBSTR(SQLERRM, 1, 512)更稳妥 - 在
WHEN OTHERS THEN分支里,必须先记录SQLCODE和SQLERRM,再决定是否RAISE_APPLICATION_ERROR(-20001, ...)包装成业务错误 - 注意:
SQLCODE在WHEN OTHERS中才可靠;在具体异常分支(如WHEN NO_DATA_FOUND)中调用,返回值是 0,不代表没出错
最容易被忽略的是:异常变量作用域和 PRAGMA 的位置约束,以及事务状态的手动管理 —— 这两点一旦出错,轻则日志记不准,重则数据不一致。











