NO_DATA_FOUND仅在SELECT INTO未查到数据时触发,DML语句不抛此异常;必须用嵌套块捕获以保障后续逻辑执行,且需按业务含义做差异化处理。

NO_DATA_FOUND 这类异常不是靠“写得漂亮”解决的,而是靠分清错误类型、控制传播路径、留够回溯线索——否则再优雅的 EXCEPTION 块也只是一层遮羞布。
什么时候该用 WHEN NO_DATA_FOUND THEN,而不是 WHEN OTHERS THEN
直接匹配预定义异常名,是唯一能避免掩盖真实问题的方式。比如 SELECT ... INTO 没查到数据,触发 NO_DATA_FOUND(SQLCODE = +100);但若你只写 WHEN OTHERS,就可能把 VALUE_ERROR(变量太小)、INVALID_NUMBER(字符串转数字失败)甚至权限错误都吞掉。
- 必须显式列出常见业务相关预定义异常:
NO_DATA_FOUND、TOO_MANY_ROWS、DUP_VAL_ON_INDEX、ZERO_DIVIDE -
WHEN OTHERS只能放在最后,且必须记录完整上下文:SQLERRM和DBMS_UTILITY.FORMAT_ERROR_BACKTRACE(11g+ 推荐) - 不要在
WHEN OTHERS里只写NULL或空DBMS_OUTPUT——这等于主动丢弃故障信号
RAISE_APPLICATION_ERROR 的参数边界和业务语义怎么对齐
自定义错误码必须落在 -20000 到 -20999 范围内,但光合规不够。真正影响维护效率的是错误信息是否带可操作线索:
- 错误信息里避免模糊词:“数据异常” → 改成“员工编号
P_EMPNO为负值,不合法” - 同一业务模块尽量复用错误码,比如所有主键校验失败统一用
-20001,所有状态流转冲突用-20002 - 不要在
RAISE_APPLICATION_ERROR中拼接动态 SQL 字符串,容易触发ORA-06502(缓冲区太小)
事务一致性被 EXCEPTION 块悄悄破坏了怎么办
PL/SQL 异常块本身不自动回滚事务。如果存储过程里先执行了 INSERT,然后在后续逻辑中触发 NO_DATA_FOUND 并被捕获,那前面的插入仍处于未提交状态——调用方可能完全不知情。
- 所有含 DML 的过程,只要没显式
COMMIT,就必须在WHEN OTHERS或关键异常分支末尾加ROLLBACK - 更稳妥的做法:在入口处设保存点
SAVEPOINT sp_start,异常时ROLLBACK TO sp_start,避免影响外层事务 - 日志表写入必须在
ROLLBACK前完成,否则异常后日志也丢了
为什么 SQLCODE 和 SQLERRM 不能直接拼进日志表字段
SQLERRM 最大长度 512 字节,而 Oracle 错误堆栈(尤其嵌套调用)常超长。直接 INSERT INTO log_table VALUES (SQLERRM) 极易触发 ORA-06502。
- 入库前必须截断:
SUBSTR(SQLERRM, 1, 500) - 想保留完整堆栈,改用
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE(返回调用链),它比SQLERRM更准,且长度可控 -
SQLCODE是数字,但注意:成功时是0,NO_DATA_FOUND是+100(正数!),其他基本都是负数——判断时别漏掉正号
真正难的不是写 EXCEPTION 块,是让每个异常分支都清楚回答三个问题:这是谁的错?影响到哪了?下次怎么提前挡住? 没有日志、没有回滚、没有分类捕获的异常处理,只是把崩溃延后了一秒。


















