Oracle PL/SQL开发基础(第三十六弹:异常处理的一些经验)

来源:互联网 发布:领啦试用网站源码 编辑:程序博客网 时间:2024/06/05 17:28

从异常中恢复

当触发异常以后,代码的执行立即跳转到异常处理部分,异常处理部分结束后,语句块就终止了。如果在处理完异常之后想继续执行抛出异常的地方后面的代码,可以将异常包装在子块中,在子块中写对应的异常控制程序,这样一旦在子块中有错误发生,子块内部的异常处理器就捕获异常。当子块结束时,就可以继续执行外层块中的下一条语句。

获取异常抛出的位置

如下代码:

DECLARE    ...BEGIN     SELECT sal INTO v_sal1 FROM emp WHERE empno = '1';    SELECT sal INTO v_sal2 FROM emp WHERE empno = '2';    SELECT sal INTO v_sal3 FROM emp WHERE empno = '3';EXCEPTION    WHEN NO_DATA_FOUND THEN        ...END;

当触发了NO_DATA_FOUND异常时,怎么知道是哪个SELECT语句触发了异常呢?

方法一:使用计数器

如:

DECLARE    v_count NUMBER := 1;BEGIN     SELECT sal INTO v_sal1 FROM emp WHERE empno = '1';    v_count  := 2;    SELECT sal INTO v_sal2 FROM emp WHERE empno = '2';    v_count  := 3;    SELECT sal INTO v_sal3 FROM emp WHERE empno = '3';EXCEPTION    WHEN NO_DATA_FOUND THEN        ...END;

触发异常时,根据计数器就能跟踪到时哪个语句触发了异常。

方法二:使用子块

将每一个SELECT语句分别定义到一个子块中,每个子块中都可以捕获异常。如:

DECLARE    ...BEGIN     BEGIN        SELECT sal INTO v_sal1 FROM emp WHERE empno = '1';    EXCEPTION        WHEN NO_DATA_FOUND THEN            ...    END;    BEGIN        SELECT sal INTO v_sal2 FROM emp WHERE empno = '2';    EXCEPTION        WHEN NO_DATA_FOUND THEN            ...    END;    BEGIN        SELECT sal INTO v_sal3 FROM emp WHERE empno = '3';    EXCEPTION        WHEN NO_DATA_FOUND THEN            ...    END;EXCEPTION    WHEN NO_DATA_FOUND THEN        ...END;

方法三:使用DBMS_UTILITY.FORMAT_ERROR_BACK_TRACE函数

这个函数是Oracle 10g以后的版本提供的。在异常处理程序中调用这个函数来访问异常的栈,它可以返回错误发生的行号。

异常与事务处理

抛出一个异常并不会终止一个事务,除非显式的调用了ROLLBACK或COMMIT。但是这里有一个问题,如果顶层的语句块存在一个未处理的异常,该异常将被传递到调用环境,那么事务将被服务器端自动回滚。
如果想在异常发生后,不放弃事务,不进行回滚,重新再处理一次,可以按如下三步来实现:
1. 将事务放在一个子块中。
2. 把子块放入一个循环,重复执行事务。
3. 在开始事务之前标记一个保存点,如果事务执行成功,就提交事务并退出循环,如果执行失败,就将控制权交给异常处理程序,事务回滚到保存点,然后重新执行事务。

如:

BEGIN    LOOP        BEGIN            SAVEPOINT 开始事务;            ...            IF ... THEN                RAISE e_some_exception;            END IF;            ...            COMMIT;            EXIT;        EXCEPTION            WHEN e_some_exception THEN                ROLLBACK TO 开始事务;                ...  -- 这里可以为一些变量重新赋值等等,重新开始循环执行。