PL/SQL中错误的异常处理


PL/SQL中对于错误的处理是很重要的一个部分,就跟写程序中对于异常的处理一样。可能程序中正常的流程实现部分不是很复杂,但是对于各种可能发生的异常情况都需要面面俱到的处理要占一半以上的代码量。

首先PL/SQL中的异常处理需要重视,但是异常的处理绝大多数的人写PL/SQL的时候都不够重视,通常在数据处理完之后对于异常的处理都是以如下的形式结尾:

when others then
  dbms_output.put_line('error......');

如果你也是这样的习惯来用when others,就需要注意了。tom大师在自己的书《深入数据库体系结构》也做了详细的解释,下面标黄的这句话让人很有感触。

  • In my experience, error handling is done wrong more often than it is done right
  • You don’t want to catch all exceptions
  • You want to catch specific exceptions you are expecting (which means they aren’t exceptions)
  • You should ignore all others – or of you do catch them to clean up a resource, RE-RAISE them

Oracle的PL/SQL小组在新版本的改进中曾希望他提出3点建议,他的第一点建议就是去除when others,最后这个部分还是保留了,但是如果你的PL/SQL中when others没有后面没有raise 或者raise_application_error的部分,就会给出警告。

SQL> alter session set plsql_warnings='enable:all';
 Session altered.

SQL> create or replace procedure test_proc
  2  as
  3  begin
  4  dbms_output.put_line('this is a test');
  5  exception
  6  when others
  7  then
  8  dbms_output.put_line('error handling...');
  9  end;
  10  /

SP2-0804: Procedure created with compilation warnings

SQL> show errors
 Errors for PROCEDURE TEST_PROC:

LINE/COL ERROR
 -------- -----------------------------------------------------------------
 1/1      PLW-05018: unit TEST_PROC omitted optional AUTHID clause;
          default value DEFINER used

6/6      PLW-06009: procedure "TEST_PROC" OTHERS handler does not end in
        RAISE or RAISE_APPLICATION_ERROR


简单解释一下原因,就是因为在PL/SQL的执行中,打个比方,有两个Insert子句。
insert into t values(1);
 insert into t values(2);

在PL/SQL中是这样执行的
savepoint insert_point1;
 insert into t values(1);
 if error then rollback to insert_point1;
 savepoint insert_point2;
 insert into t values(2);
 if error then rollback to insert_point2;
这个步骤就相当于在一个事物中,每一个原子操作如果失败,都会在错误处理中进行rollback;
但是如果你在数据处理中,已经显式做了事物提交,那么你在错误处理的时候再rollback就晚了,前一部分已经提交了。
 这个情景就类似下面的场景
insert into t values(1);
 commit;
 insert into t values('xxxxx');  --这个语句会报错
 那么在整个事物的处理中第一部分已经提交了,第二部分出错了也没办法做回滚了。
 在when others 中加入了rollback也无济于事。

 可能大家在更多的错误处理中都是简单把错误信息打印出来而已,这样的处理结果相当于你重写了错误的处理方法,
 下面隐式的错误处理就被覆盖了,除非你定义了合理的错误处理场景,使用raise或者raise_application_error对错误做了正确的处理。
if error then rollback to insert_point1;
 if error then rollback to insert_point2;

所以在错误的处理中还是建议不要使用when others 子句。这种笼统的错误处理可能更多的时候会造成数据的不一致。
 如果一定想要使用when others then
可以采用类似下面的形式。
begin
 savepoint sp;
 process;--这个部分就是具体的处理代码
exception
 when others then
    rollback to sp;
    dbms_output.put_line('Error,||sqlerrm);
 end;
 /
这样即使代码中的某一部分出现异常,也能保证整个事物是一个原子操作。

rlwrap - 解决Linux下SQLPLUS退格、上翻键乱码问题

SQLPLUS spool 到动态日志文件名

Oracle SQLPLUS提示符设置

通过设置SQLPLUS ARRAYSIZE(行预取)加快SQL返回速度

PL/SQL Developer实用技巧分享

相关内容