WHEN OTHERS 是捕获 ORA-00001 的唯一入口,因其非预定义异常,须用 SQLCODE = -1 判断或 PRAGMA EXCEPTION_INIT 映射;日志写入需自治事务,且 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE 应在异常处理开头调用。

WHEN OTHERS 是捕获 ORA-00001 的唯一入口
Oracle 存储过程中不能写 WHEN ORA-00001 THEN,也不支持 WHEN SQLCODE = -1 THEN——这些语法直接报编译错误。所有以 ORA- 开头的错误码(如 ORA-00001、ORA-02291)都不是 PL/SQL 预定义异常名,必须兜底到 WHEN OTHERS 分支里再做判断。
常见错误现象:
- 写
WHEN -1 THEN或WHEN SQLCODE = -1 THEN→ 编译失败:PLS-00367: a label cannot be used in this context - 依赖
SQLERRM字符串匹配'ORA-00001'→ 在某些字符集或日志截断场景下失效(比如SQLERRM被截成"ORA-00001: unique constraint..."前半段)
正确做法是只比对 SQLCODE 数值:
-
ORA-00001对应SQLCODE = -1(注意是负数,且无前导零) -
ORA-02291对应SQLCODE = -2291 -
NO_DATA_FOUND是预定义异常,对应SQLCODE = 100,但建议仍用WHEN NO_DATA_FOUND THEN直接捕获,更语义清晰
用 PRAGMA EXCEPTION_INIT 映射非预定义错误码
如果你希望把某个 ORA 错误码变成可读的异常变量名(比如 e_dup_key),就得用 PRAGMA EXCEPTION_INIT 显式绑定。它不改变执行逻辑,只让 WHEN 分支更易维护。
示例:
DECLARE
e_dup_key EXCEPTION;
PRAGMA EXCEPTION_INIT(e_dup_key, -1);
e_fk_violation EXCEPTION;
PRAGMA EXCEPTION_INIT(e_fk_violation, -2291);
BEGIN
INSERT INTO users (id, name) VALUES (1, 'Alice');
EXCEPTION
WHEN e_dup_key THEN
DBMS_OUTPUT.PUT_LINE('主键或唯一约束冲突');
WHEN e_fk_violation THEN
DBMS_OUTPUT.PUT_LINE('外键引用不存在的记录');
WHEN OTHERS THEN
RAISE; -- 其他错误不吞掉
END;注意点:
-
PRAGMA EXCEPTION_INIT必须在声明区,且紧挨着对应异常变量声明 - 它只能绑定 Oracle 内置错误码(如
-1、-2291),不能用于自定义错误码(如-20001)——后者要用RAISE_APPLICATION_ERROR抛出 - 一个异常变量只能绑定一个错误码;一个错误码不能绑定多个变量
写日志必须加自治事务(PRAGMA AUTONOMOUS_TRANSACTION)
在 WHEN OTHERS 里往日志表(如 TBL_PROC_ERRMSG)插入记录并 COMMIT,若不加自治事务,会直接破坏主事务一致性,甚至触发 ORA-06519 错误。
典型错误流程:
- 主存储过程在事务中执行 DML → 出现
ORA-00001 - 进入
WHEN OTHERS→ 执行INSERT INTO TBL_PROC_ERRMSG ...; COMMIT; - 此时主事务被强制提交或回滚 →
ORA-06519: active autonomous transaction detected and rolled back
正确写法:
- 日志写入逻辑单独封装为一个过程,开头加
PRAGMA AUTONOMOUS_TRANSACTION; - 该过程内必须显式
COMMIT或ROLLBACK,否则会报错 - 不要在主过程的
EXCEPTION块里直接INSERT+COMMIT,哪怕只是测试也容易埋坑
简写示意:
CREATE OR REPLACE PROCEDURE log_error(p_code NUMBER, p_msg VARCHAR2) IS PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO TBL_PROC_ERRMSG (ERR_CODE, MSG) VALUES (p_code, p_msg); COMMIT; -- 必须有 END;
SQLCODE 和 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE 的配合使用
单靠 SQLCODE 只能知道错误类型,定位不到具体哪行代码出问题。真正排障靠的是 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE,它返回的是异常实际发生的 PL/SQL 行号(不是 EXCEPTION 块所在行),比 SQLERRM 里的 “ORA-06512: at line X” 更稳定。
使用建议:
- 日志中至少记录:
SQLCODE、SQLERRM、DBMS_UTILITY.FORMAT_ERROR_BACKTRACE - 避免只用
SQLERRM—— 它可能被 NLS 设置影响格式,也可能被截断 -
DBMS_UTILITY.FORMAT_ERROR_STACK和SQLERRM功能重复,一般不用 - 如果调用链深(比如 A → B → C),
FORMAT_ERROR_BACKTRACE只显示最内层出错位置,不显示调用路径;需要调用栈才用FORMAT_CALL_STACK
最容易被忽略的一点:在 WHEN OTHERS 里调用 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE 之前,不能有任何 DML 或 DDL 操作——否则堆栈信息会被覆盖。所以日志写入必须放在最开头,或者封装进自治事务过程里一气呵成。


















