可行但必须配合SQLCODE和SQLERRM获取真实错误信息;单独使用WHEN OTHERS仅捕获“有错”而丢失“什么错”,且二者仅在EXCEPTION块内有效,须立即消费不可跨作用域调用。
直接用 when others then 捕获所有未处理异常是可行的,但必须配合 sqlcode 和 sqlerrm 才能拿到真实错误信息;单独写 when others 而不读取这两个值,等于只捕获了“有错”,却丢掉了“什么错”。
WHEN OTHERS 不等于自动记录错误
很多人以为写了 WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('出错了') 就算处理了异常,其实这只是掩盖问题。PL/SQL 中 WHEN OTHERS 本身不提供错误编号或消息,必须显式调用 SQLCODE(返回负数错误号,如 -1403)和 SQLERRM(返回带 ORA- 前缀的完整字符串,如 ORA-01403: no data found)才能还原现场。
-
SQLCODE是数字,SQLERRM是字符串,两者必须一起用才有诊断价值 -
SQLERRM默认长度可能超 4000 字符,入库前建议用SUBSTR(SQLERRM, 1, 3999)截断 - 在自治事务(
AUTONOMOUS_TRANSACTION)中记录日志时,SQLCODE/SQLERRM仍有效,且不受主事务回滚影响
常见误用:在异常块外读取 SQLCODE
SQLCODE 和 SQLERRM 只在 EXCEPTION 块内有效。一旦离开该作用域(比如跳到另一个 procedure 或函数里),它们就退化为 0 和空字符串 —— 这不是 bug,是 PL/SQL 的设计机制。
- 错误写法:
DECLARE v_code NUMBER; BEGIN ... EXCEPTION WHEN OTHERS THEN v_code := SQLCODE; my_log_proc(v_code); END;→my_log_proc内再读SQLCODE会得到 0 - 正确做法:所有基于错误信息的逻辑(插入日志表、拼接告警内容、决定是否重试)必须在
EXCEPTION块内完成 - 若需复用日志逻辑,把参数全传进去:
log_error(v_code, v_msg, SYSDATE);,不要在里面二次调用SQLCODE
SQLCODE 返回值的几个关键细节
SQLCODE 看似简单,但有几个边界行为容易踩坑:
- 成功执行时返回 0,但
WHEN OTHERS永远不会触发于成功路径,所以你在EXCEPTION块里见到的SQLCODE永远非零 - 自定义错误(
RAISE_APPLICATION_ERROR(-20001, 'xxx'))返回你指定的负数,比如 -20001;它不会被转成 ORA-20001,SQLERRM里仍是原样字符串 - 当嵌套块中发生异常,外层
WHEN OTHERS捕获时,SQLCODE是最内层抛出的那个值,不会叠加或覆盖 -
NO_DATA_FOUND对应SQLCODE = -1403,但反过来,SQLCODE = -1403不一定来自SELECT ... INTO—— 某些 UDF 或 Java callout 也可能抛这个码
真正难的不是语法,而是确保每次 WHEN OTHERS 出现时,SQLCODE 和 SQLERRM 都被立刻消费掉,而不是被忽略、被截断、被跨作用域误读。日志表字段类型、字符集、NLS_DATE_FORMAT 都可能让 SQLERRM 存储失真 —— 这些细节比会不会写 EXCEPTION 块更决定线上问题能否快速定位。











