【DB.PL/SQL】程序流程控制 —— 异常处理

如果PL/SQL 发生了一个错误,无论是系统错误还是应用的错误,都会抛出一个异常。当前PL/SQL块中执行单元就会暂定处理,如果当前块有一个异常处理单元的话,控制会转移到当前块的异常处理单元来处理异常。完成了异常处理后就不能再返回到当前块,相反,控制会转移到外层包围块,如果有的话。

异常分类:

  • 系统异常
  • 程序员定义异常

定义异常

定义异常名称

语法如下:

exception_name EXCEPTION;


有两种引用方式:

  • 在程序的执行单元中的抛出异常RAISE:

RAISE exception_name;


  • 在异常处理单元中的WHEN语句:

WHEN exception_name THEN

异常名称与错误代码关联

Oracle只给一小部分异常定义了名字;数据库里还有其他的错误只定义了一个错误数字和消息;

可以使用EXCEPTION_INIT编译命令,把异常名称和内部错误代码关联在一起:

例如:

PROCEDURE my_procedure
IS
    invalid_month EXCEPTION;
    PRAGMA EXCEPTION_INIT(invalid_month, -1843);
BEGIN
    ...
EXCEPTION 
    WHEN invalid_month THEN
	


错误数字可以使用符合下列约束的任意整数:

  • 不可以使用 -1403
  • 不可以使用0 或者除了 100 之外的任何正数
  • 不可以使用小于 -1000000 的负数


命名的系统异常

最重要的也是最常用的命名异常可以在PL/SQL的 STANDARD 包中找到。


例如,代码中处理 NO_DATA_FOUND 异常:

WHEN NO_DATA_FOUND THEN
或者
WHEN STANDARD.NO_DATA_FOUND THEN


STANDARD包中预定义的一些异常


异常的作用范围

  • 被命名的系统异常 —— 全局可用;可以在任何代码中抛出和处理被命名的系统异常
  • 被命名的程序定义的异常 —— 只能在声明它的块的执行单元,异常处理单元被抛出和处理
  • 匿名系统异常 —— 可以在任何PL/SQL块的异常处理单元的WHEN OTHERS 部分处理。如果这些成为了被指定名字的异常,这个名称的作用范围和被命名的自定义异常时一样的
  • 匿名的自定义异常 —— 只在调用 RAISE_APPLICATION_ERROR 时才定义,并返回到调用程序


抛出异常

有三种方法抛出异常:

  • 数据库检测到错误时可以抛出异常
  • 使用RAISE语句抛出异常
  • 使用内置的RAISE_APPLICATION_ERROR过程抛出异常


使用RAISE语句

语法如下:

RAISE exception_name;
RAISE package_name.exception_name;
RAISE;

第3种形式只可以用在异常处理单元的WHEN语句中,用于在异常处理单元中再次抛出同一个异常:

例如:

EXCEPTION
    WHEN NO DATA fOUND
    THEN
        ...
        -- 现在再把未处理的NO_DATA_fOUND 传播到外层包围块中
	RAISE;


使用RAISE_APPLICATION_ERROR过程

使用这个过程的好处在于,可以给异常加上一段错误消息。

一旦这个过程运行,当前PL/SQL块的执行就立即终止,对OUT和IN OUT参数所做的修改会被撤销,对于全局数据结构做的修改,如包变量,数据库对象不会回滚。用户必须明确调用 ROLLBACK 来撤销DML操作引起的修改。

语法如下:

PROCEDURE RAISE_APPLICATION_ERROR(
num binary_integer,
msg varchar2,
keeperorstack boolean default FALSE
);


  • num是错误号,数值必须在 -20999 和 -20000 之间
  • msg是错误消息
  • keeperorstack表明是否把这个错误添加到栈中(TRUE)还是替换现存的错误(FALSE)


处理异常

一旦有异常抛出,当前PL/SQL块就会终止正常执行,把控制传递给异常处理单元。这个异常或者被当前PL/SQL块中的处理句柄处理,或者抛给外层块。

要捕获或者处理某个异常,必须专门为这个异常写一个异常句柄。异常处理代码在程序中的位置是在所有可执行语句之后,在块的END语句之前。

EXCEPTION关键字指示了异常处理单元的开始以及每个独立的异常句柄:

DECLARE
   ...declarations...
BEGIN
    ...executable statements...
[ EXCEPTION
     ...exception handles... ]
END;


异常句柄(exception handles)的语法:

WHEN exception_name [ OR exception_name ... ]
THEN
    executable statements
或者
WHEN OTHERS
THEN
    executable statements

  • 一个异常处理单元可以有多个异常句柄。
  • 可以通过OR操作符在一个 WHEN 语句中整合多个异常(不能使用AND)
  • WHEN 语句只能根据异常名称而不能根据错误代码来捕获异常。
  • WHEN OTHERS语句是可选的;WHEN OTHERS语句必须是异常处理部分的最后一个句柄。如果其后还有WHEN语句,会收到编译错误。


内置的错误函数

  • SQLCODE —— 返回代码中最后一次抛出的错误;如果没有任何错误,返回0;如果在异常句柄之外调用,也返回0
  • SQLERRM —— 返回某个错误代码对应的错误信息。若没有给其传递错误代码,就会返回SQLCODE的错误代码对应的消息
  • DBMS_UTILITY.FORMAT_ERROR_STACK —— 和SQLERRM类似,但其只返回SQLCODE错误代码关联的消息。不能用来获得任意错误代码的消息
  • DBMS_UTILITY.FORMAT_ERROR_BACKTRACE —— 返回一个格式化的文本串,这个文本串即程序栈,以及能够回退到错误最初发生代码行的行号
  • DBMS_UTILITY.FORMAT_CALL_STACK —— 显示PL/SQL程序的调用堆栈


未处理异常


处理异常后继续执行后续代码的方法

一旦PL/SQL块中抛出了异常,正常执行单元被终止然后控制传递到异常处理单元。一旦块中有异常抛出就再也不能回到该块的执行单元了。

然而,在某些时候,我们想要异常过后还能继续执行的能力。

采用如下方法,可以保证未抛出异常的语句都有机会得到执行

例如:

PROCEDURE change_date IS
BEGIN
    BEGIN
        DELETE FROM employees WHERE...;
    EXCEPTION
	WHEN OTHERS THEN log_error;
    END;
	
    BEGIN	
        UPDATE company SET...;
    EXCEPTION
	WHEN OTHERS THEN log_error;
    END;
	
    BEGIN
        INSERT INTO company_history SELECT • FROM company WHERE...;
    EXCEPTION
	WHEN OTHERS THEN log_error;
    END;
END;

在执行单元抛出的异常总是在当前块中被处理 —— 如果匹配的句柄存在。通过给任何语句加上一个BEGIN,之后加上一个EXCEPTION单元和END语句构建一个“虚拟块”出来,可以在代码中控制异常的失败范围。


WHEN OTHERS的处理

WHEN OTHERS语句可以捕获所有其他的未处理异常。

可以利用内置异常函数比如SQLCODE 和 DBMS_UTILITY.FORMAT_ERROR_STACK获得发生的错误的信息。












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值