发布时间:2026-08-31 17: 29: 00
PL/SQL可以通过【EXCEPTION】区处理运行过程中出现的异常,包括Oracle预定义异常、没有预定义名称的内部异常以及用户自定义异常。常见的【NO_DATA_FOUND】【TOO_MANY_ROWS】【DUP_VAL_ON_INDEX】【ZERO_DIVIDE】可以直接捕获,自定义业务异常则需要声明并主动触发。出现程序已经编写异常处理但错误仍直接返回调用端时,应重点检查异常发生在哪个代码块、处理器捕获的异常名称是否一致,以及异常是否在声明区或异常处理区再次产生。
一、PL/SQL怎么设置异常处理
PL/SQL异常处理通常写在当前程序块的【EXCEPTION】区域,并按照具体异常优先、【WHEN OTHERS】兜底的方式组织。需要识别指定ORA错误时,可以通过【PRAGMA EXCEPTION_INIT】给错误码绑定名称。
1、处理Oracle预定义异常
①找到可能产生异常的【BEGIN】代码区域。
②在正常业务语句之后增加【EXCEPTION】。
③使用【WHEN NO_DATA_FOUND THEN】处理查询不到数据的情况。
④可能返回多条记录时增加【WHEN TOO_MANY_ROWS THEN】。
⑤唯一约束冲突可以使用【WHEN DUP_VAL_ON_INDEX THEN】。
⑥除零问题可以使用【WHEN ZERO_DIVIDE THEN】。
⑦在各处理分支中记录错误、回滚事务或返回业务状态。
⑧最后根据业务要求决定是否继续向上抛出异常。
预定义异常已经在PL/SQL的【STANDARD】包中声明,不需要再次自行定义。如果重新声明同名异常,局部名称会覆盖原来的预定义名称,反而可能导致对应处理器无法捕获实际Oracle异常。
2、处理指定ORA错误
有些数据库错误拥有错误码,但没有可直接使用的预定义异常名称,这时可以自行建立异常名称。
①在过程、函数或匿名块的声明部分定义异常,例如【deadlock_detected EXCEPTION】。
②紧接着使用【PRAGMA EXCEPTION_INIT】。
③把异常名称与对应Oracle错误码绑定,例如死锁错误使用【-60】。
④在【EXCEPTION】区域增加【WHEN deadlock_detected THEN】。
⑤在处理分支记录当前事务信息。
⑥根据业务要求执行【ROLLBACK】或其他恢复处理。
⑦需要让上层继续感知异常时执行【RAISE】。
【EXCEPTION_INIT】中的Oracle错误码通常使用负数形式,配置前应先确认实际ORA编号,不能根据错误文字猜测。
3、设置用户自定义异常
①在声明区域定义【业务异常名EXCEPTION】。
②在业务判断中检查需要拦截的条件。
③条件成立时执行【RAISE业务异常名】。
④在当前代码块的【EXCEPTION】区域增加对应【WHEN】分支。
⑤需要向调用程序返回自定义错误码时,可以使用【RAISE_APPLICATION_ERROR】。
⑥自定义错误码使用Oracle规定的【-20000】到【-20999】范围。
⑦在错误信息中写明具体业务原因。
这种方式适合库存不足、参数范围错误、业务状态不允许修改等数据库自身不会自动产生ORA异常的情况。
二、PL/SQL异常没有被捕获如何排查
异常没有进入预期的【WHEN】分支时,首先要确认异常究竟发生在哪个PL/SQL块。异常处理存在明确作用域,某个内层Block捕获不到的异常会向外传播,但并不是任何外层代码都能继续使用原来的异常名称。
1、检查异常发生的代码块层级
①找到实际抛出错误的语句。
②确认该语句位于哪个【BEGIN】和【END】之间。
③检查这个Block是否存在【EXCEPTION】区域。
④确认对应【WHEN】分支属于同一个Block或它的外层Block。
⑤内层Block没有对应处理器时,异常会继续向外传播。
⑥逐层检查外层过程、函数和匿名块。
⑦如果一直没有匹配处理器,异常最终会返回调用端。
处理器不是按照源文件中的距离匹配,而是按照PL/SQL块的嵌套关系查找,所以异常代码和【WHEN】看起来位置接近,也不代表它们处于同一个作用域。
2、检查异常是不是发生在声明区域
这一类情况很容易被误判为“EXCEPTION没有执行”。
①检查【DECLARE】或过程声明区中的变量初始化。
②查看类型转换、函数调用和常量赋值是否可能报错。
③如果异常在当前Block的声明区域产生,不会进入这个Block自己的异常处理器。
④在外层再增加一个PL/SQL Block。
⑤由外层【EXCEPTION】处理声明阶段产生的异常。
⑥重新执行并观察捕获位置。
例如变量精度过小导致初始化阶段产生【VALUE_ERROR】,当前块后面的EXCEPTION无法处理,只能继续向外围传播。
3、检查异常是否在处理器中再次被抛出
①进入实际执行到的【WHEN】分支。
②检查其中是否调用了其他过程或SQL。
③确认这些语句有没有产生新的异常。
④查看是否存在【RAISE】。
⑤检查是否调用了【RAISE_APPLICATION_ERROR】。
⑥如果处理器内部再次产生异常,新异常会直接向外层传播。
⑦在更外层Block增加对应处理逻辑进行验证。
因此看到调用端收到异常,并不一定表示最里面的处理器从未运行,也可能是处理完成后主动重新抛出了错误。
三、异常仍然无法定位时怎么继续检查
代码块和异常类型确认无误后,可以直接记录Oracle返回的错误码、错误栈和异常产生行号,再根据实际错误决定应该增加哪个处理器。
1、记录SQLCODE和SQLERRM
①在【WHEN OTHERS THEN】中获取【SQLCODE】。
②同时获取【SQLERRM】。
③把错误码和错误信息写入日志表或调试输出。
④重新触发一次异常。
⑤确认实际ORA错误是否和原来预计的一致。
⑥再决定是否改用具体异常处理器。
【WHEN OTHERS】适合兜底和定位未知错误,但不建议捕获以后什么都不做,否则上层程序可能误认为执行成功。
2、查看异常真正产生的位置
①在异常处理区获取【DBMS_UTILITY.FORMAT_ERROR_BACKTRACE】。
②同时记录【DBMS_UTILITY.FORMAT_ERROR_STACK】。
③查看错误最初发生的过程和代码行。
④沿调用栈检查对应存储过程、函数或触发器。
⑤找到真正产生异常的SQL或PL/SQL语句。
⑥修正后重新执行。
【FORMAT_ERROR_BACKTRACE】能够保留异常最初产生的位置,比只看最外层返回的错误行更适合排查多层过程调用。
3、检查自定义异常作用域
①找到自定义异常的声明位置。
②确认外层处理器是否能够访问该异常名称。
③如果异常只在内层Block声明,传播到外层后原名称已经超出作用域。
④需要外层按名称捕获时,把异常声明提升到外层或Package范围。
⑤无法调整声明范围时,在外层使用【WHEN OTHERS】并结合错误码判断。
⑥重新测试异常传播过程。
总结
PL/SQL设置异常处理时,可以使用预定义异常直接处理常见Oracle错误,通过EXCEPTION_INIT绑定指定错误码,也可以声明用户异常并通过RAISE主动触发。异常没有被捕获时,应优先检查代码块作用域、声明区域异常和异常处理器中的二次抛出,再利用SQLCODE、SQLERRM和错误回溯确定真正的异常位置。把异常发生位置和处理器所属Block对应起来,比单纯增加WHEN OTHERS更容易找到问题。如需进一步了解PL/SQL异常处理、异常传播与错误定位方法,欢迎联系咨询。
展开阅读全文
︾