必须使用 raise_application_error 才能让客户端识别业务错误号,错误号限于-20000至-20999,需非空且≤2048字节消息,仅可在pl/sql中调用,keep_errors参数慎用,错误分类应基于sqlcode而非消息文本。

必须用 RAISE_APPLICATION_ERROR 才能让 Java/Python 等客户端拿到可识别的业务错误号,仅用 RAISE 会丢失语义、无法分类处理。
错误号只能是 -20000 到 -20999 之间的整数
超出这个范围会导致编译失败,报 PLS-00302: component 'RAISE_APPLICATION_ERROR' must be declared——不是函数没引入,而是编号非法被 Oracle 拒绝。常见踩坑点包括:
- 误用系统错误号(如
-1、-1403、-1) - 动态拼接错误号但没校验范围(比如从配置表读取后直接传入)
- 写成正数(如
20001)或带小数(如-20000.5)
建议在项目中定义常量包统一管理,例如:
CREATE OR REPLACE PACKAGE err_code AS EMP_NOT_FOUND CONSTANT PLS_INTEGER := -20001; DUP_USERNAME CONSTANT PLS_INTEGER := -20002; INSUFFICIENT_BAL CONSTANT PLS_INTEGER := -20003; END;
错误消息不能为空,且不能超 2048 字节
RAISE_APPLICATION_ERROR 的第二个参数是 VARCHAR2,要求非空、长度 ≤2048 字节。常见问题:
- 传空字符串
''→ 报ORA-20000:(只有冒号,无内容) - 拼接过长日志(如把
SQLERRM和完整堆栈都塞进去)→ 被静默截断,关键信息丢失 - 未做
NVL或TRIM处理导致首尾空格干扰判断
推荐写法:
RAISE_APPLICATION_ERROR( err_code.DUP_USERNAME, '用户名 [' || NVL(v_username, '<null>') || '] 已存在' );</null>
不能在 SQL 表达式里直接调用
RAISE_APPLICATION_ERROR 是 PL/SQL 过程,只能在 PL/SQL 块、存储过程、函数、触发器、匿名块中使用。以下写法均非法:
- 在
SELECT中调用:SELECT RAISE_APPLICATION_ERROR(-20001, 'no') FROM DUAL - 在
CASE表达式中嵌套 - 作为函数返回值的一部分(除非封装在函数体内)
如果需要在 SQL 层做校验,应改用约束、触发器或提前在 PL/SQL 层拦截。例如,插入前查重并显式抛出:
IF v_count > 0 THEN RAISE_APPLICATION_ERROR(err_code.DUP_USERNAME, '用户名重复'); END IF;
第三个参数 keep_errors 很少要用,慎开
该布尔参数控制是否保留已有错误堆栈,默认为 FALSE(覆盖)。设为 TRUE 时,新错误会追加到当前错误列表,但实际效果受限于 Oracle 版本和调用上下文:
- 在大多数 JDBC 场景下,客户端只收到最外层错误号和消息,
keep_errors => TRUE几乎不可见 - 启用后可能让错误堆栈更混乱,尤其在多层异常嵌套时
- 除非明确需要向 DBA 提供完整链路(如审计日志),否则不建议开启
真正需要透出多层原因时,更稳妥的做法是拼接进消息体:
RAISE_APPLICATION_ERROR( err_code.INSUFFICIENT_BAL, '余额不足:当前' || v_balance || ',需' || v_required || ';原始错误:' || SQLCODE || '-' || SQLERRM );
容易被忽略的一点:错误号是客户端唯一可靠的分类依据,消息文本可能被国际化、截断或前端二次加工,所以业务逻辑分支必须基于 SQLCODE(即你传的 -20xxx)判断,而不是解析错误字符串。











