博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【DB.PL/SQL】程序流程控制 —— 异常处理
阅读量:4005 次
发布时间:2019-05-24

本文共 3331 字,大约阅读时间需要 11 分钟。

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

异常分类:

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

定义异常

定义异常名称

语法如下:

exception_name EXCEPTION;

有两种引用方式:

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

RAISE exception_name;

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

WHEN exception_name THEN

异常名称与错误代码关联

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

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

例如:

PROCEDURE my_procedureIS    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 OTHERSTHEN    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 ISBEGIN    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获得发生的错误的信息。

转载地址:http://mxwfi.baihongyu.com/

你可能感兴趣的文章
如何优雅、机智地和新公司谈薪水?
查看>>
为什么读了很多书,却学不到什么东西?
查看>>
长文干货:如何轻松应对工作中最棘手的13种场景?
查看>>
如何确保自己的Mac数据安全呢?这里有四个“小秘诀”
查看>>
如何用好碎片化时间,让思维更有效率?
查看>>
第一性原理:戳中问题本质的人是怎么思考的?
查看>>
No.147 - LeetCode1108
查看>>
No.148 - LeetCode771
查看>>
No.174 - LeetCode1305 - 合并两个搜索树
查看>>
No.175 - LeetCode1306
查看>>
No.176 - LeetCode1309
查看>>
FE:http状态码
查看>>
No.182 - LeetCode1325 - C指针的魅力
查看>>
mac:移动python包路径
查看>>
mysql:sql create database新建utf8mb4 数据库
查看>>
mysql:sql alter database修改数据库字符集
查看>>
mysql:sql alter table 修改列属性的字符集
查看>>
mysql:sql drop table (删除表)
查看>>
mysql:sql truncate (清除表数据)
查看>>
scrapy:xpath string(.)非常注意问题
查看>>