sqlcode和sqlerrm用于捕获oracle运行时错误代码与消息,sqlcode返回错误号(如ora-01403对应+100,用户异常为+1,其余多为负数),sqlerrm返回带前缀的错误文本,二者须在exception块中配合when others使用并输出,否则无法定位错误。
直接看 sqlcode 和 sqlerrm 就能定位绝大多数运行时错误,但前提是异常处理块里写了输出逻辑;没写就只能靠 show errors 查编译错,或靠 dbms_utility.format_error_backtrace() 看真实行号。
编译失败先查 show errors,别急着改代码
执行 CREATE OR REPLACE PROCEDURE 后提示“Warning: Procedure created with compilation errors”,第一反应不是重写,而是立刻运行:
show errors procedure your_proc_name;
它会返回具体哪一行、什么错误,比如 PLS-00201: identifier 'EMP_TABLE' must be declared —— 这大概率是表名拼错、没加 schema 前缀,或当前用户没权限。此时该查 ALL_OBJECTS 和 ALL_TAB_PRIVS,而不是翻存储过程里几十行 SQL。
- 如果错误是
PLS-00302: component 'XXX' must be declared,检查变量/游标是否在DECLARE段声明,且作用域没越界 - 若报
PL/SQL: ORA-00942: table or view does not exist,确认对象是否存在、是否用了全限定名(如scott.emp),同义词是否生效 - 包体编译失败时,重点核对包头(spec)和包体(body)中函数/过程的参数个数、类型、顺序是否完全一致
运行时报错必须用 EXCEPTION 块捕获,否则看不到上下文
很多开发者只写 WHEN OTHERS THEN NULL;,等于把错误吞掉。真要排查,得让异常“说话”:
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('查询无结果:' || SQLERRM);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('错误码:' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('错误信息:' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('堆栈:' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE());
END;
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE() 返回的是异常实际发生的行号(不是 EXCEPTION 块所在行),比 SQLERRM 更准。注意:它不显示调用链,只返回 PL/SQL 块内出错位置。
-
SQLCODE是数字,ORA-01403对应100,ORA-00001对应1,负值才是标准 Oracle 错误码 -
SQLERRM默认带前缀(如 “ORA-01403: no data found”),传入SQLCODE可获取纯消息:SQLERRM(-2291) - 不要在
WHEN OTHERS里只写NULL或空RAISE,那会让日志断档
想捕获特定 Oracle 错误(如外键违例),得用非预定义异常 + PRAGMA EXCEPTION_INIT
预定义异常只有 21–25 个,像 ORA-02291(父键不存在)、ORA-02292(子记录存在)这种不会自动映射到名字,必须手动绑定:
DECLARE
e_integrity EXCEPTION;
PRAGMA EXCEPTION_INIT(e_integrity, -2291);
BEGIN
INSERT INTO emp (deptno) VALUES (9999);
EXCEPTION
WHEN e_integrity THEN
DBMS_OUTPUT.PUT_LINE('插入失败:部门 9999 不存在');
END;
关键点不在“怎么写”,而在“怎么知道该绑哪个号”——出错后先记下完整 ORA-xxxxx,再查文档或运行 SELECT * FROM V$ERROR WHERE ERROR_NUMBER = -2291;(部分版本支持)。
-
PRAGMA EXCEPTION_INIT必须写在声明段,且紧挨着异常变量声明 - 错误号是负数,写成
-2291,不是2291或ORA-02291 - 一个异常变量只能绑定一个错误号;多个错误需定义多个变量
自定义业务异常要用 RAISE_APPLICATION_ERROR,别用 RAISE 直接抛变量
自己定义的异常(比如“余额不足”“参数非法”)不能靠 RAISE my_excep; 透出到调用方,那样外部捕获不到错误码;必须用系统级抛出:
DECLARE insufficient_funds EXCEPTION; BEGIN IF balance <p>上面这段是典型误区:<code>insufficient_funds</code> 声明了但没被 <code>RAISE</code> 触发,而 <code>RAISE_APPLICATION_ERROR</code> 会直接终止执行并抛出带码错误,调用方可用 <code>WHEN OTHERS</code> 捕获并解析 <code>SQLCODE</code> 判断。</p>
-
RAISE_APPLICATION_ERROR的错误码必须是 -20000 到 -20999 之间 - 消息长度上限 2048 字节,超长会被截断,建议提前
SUBSTR(..., 1, 2000) - 如果希望上层统一处理某类业务错,就在调用方用
SQLCODE IN (-20101, -20102)分支判断,别依赖异常名
真正难的不是语法,是分清错误来源:编译错看 show errors,运行错靠 FORMAT_ERROR_BACKTRACE 定位行号,Oracle 系统错靠 PRAGMA EXCEPTION_INIT 绑定,业务错必须走 RAISE_APPLICATION_ERROR 打码透出——混用或漏掉任一环,排查就会卡住。











