一、异常处理
1.1 异常处理方法
1.1.1 异常介绍
在编写PL/SQL程序时,不可避免地会发生一些错误,可能是程序设计人员自己造成的,也可能是操作系统或硬件环境出错,比如出现除数为零、磁盘I/O错误等情况。对于出现的这些错误,Oracle采用异常机制来处理,异常处理代码通常放在PL/SQL的EXCEPTION代码块中。
1.1.2 异常分类
根据异常产生的机制和原理,可将Oracle系统异常分为以下两大类:
(1)预定义异常
Oracle系统自身为用户提供了大量的、可在PL/SQL中使用的预定义异常,以便检查用户代码失败的一般原因。
它们都定义在Oracle的核心PL/SQL库中,用户可以在自己的PL/SQL异常处理部分使用名称对其进行标识。
对这种异常情况的处理,用户无需在程序中定义,它们由Oracle自动引发。
(2)自定义异常
有时候可能会出现操作系统错误或机器硬件故障,这些错误Oracle系统自身无法知晓,也不能控制。例如,操作系统因病毒破坏而产生故障、磁盘损坏、网络突然中断等。
另外,因业务的实际需求,程序设计人员需要自定义一些错误的业务逻辑,而PL/SQL程序在运行过程中就可能会触发到这些错误的业务逻辑。
对于以上这些异常情况的处理,就需要用户在程序中自定义异常,然后由Oracle自动引发。
1.1.3 预定义异常处理方法
每当PL/SQL程序违反了Oracle的规则或超出系统的限制时,系统就自动地产生内部异常。每个Oracle异常都有一个号码,但异常必须按名处理。因此,PL/SQL对那些常见的异常预定义了异常名。
1.1.4 预定义异常和用户自定义异常处理方法
(1)异常声明
用户定义异常包括预定义异常和用户自定义异常,用户定义的异常只能在PL/SQL块的声明部分进行声明,声明方式与变量声明类似。
异常是一种状态而不是一个对象,因此,异常名不能出现在赋值语句或SQL语句中。
(2)抛出异常
用户定义的异常使用RAISE语句显式地提出。
(3)为内部异常命名
在PL/SQL中,必须使用OTHERS处理程序或用伪命令EXCEPTION_INIT来处理未命名的内部异常。EXCEPTION_INIT的作用是告诉编译程序将一个异常名与一个Oracle错误号码联系起来。因此,用户就可以按名称引用任何内部异常,并为它编写一个特
1.2 异常处理语法
1.2.1 声明异常
语法:
exception_name EXCEPTION;
其中,exception_name为用户定义的异常名。
1.2.2 为内部异常命名
语法:
PRAGE EXCEPTION_INIT(exception_name, ORA_errornumber);
其中,ORA_errornumber为用户定义的Oracle错误号。
1.2.3 异常定义
示例:
DECLARE
--声明异常
exceprion_name EXCEPTION;
BEGIN
IF condition THEN
--异常定义
RAISE exception_name;
END IF;
EXCEPTION
WHEN exception_name THEN
Statement;
END;
1.2.4 异常处理
示例:
SET SERVEROUTPUT ON --将输出流开关打开
EXCEPTION
--异常处理
WHEN exception1 THEN
statement1
WHEN exception2 THEN
statement2
……
WHEN OTHERS THEN
statement3
1.2.5 使用SQLCODE和SQLERRM函数定义提示信息
示例:
DBMS_OUTPUT.PUT_LINE('错误号:'||SQLCODE);
DBMS_OUTPUT.PUT_LINE('错误号:'||SQLERRM);
1.3 预定义异常
1.3.1 预定义异常介绍
当PL/SQL程序违反了Oracle系统内部规定的设计规范时,就会自动引发一个预定义的异常,例如,当除数为零时,就会引发ZERO_DIVIED异常。
1.3.2 Oracle 常见预定义异常
(1)ACCESS_INTO_NULL
该异常对应于ORA-06530错误。为了引用对象属性,必须首先初始化对象。当直接引用未初始化的对象属性时,会触发该异常。
(2)CASE_NOT_FOUND
该异常应用于ORA-06592错误。当CASE语句的WHEN子句没有包含必须条件分支或者ELSE子句时,会触发该异常。
(3)COLLECTION_IS_NULL
该异常应用于ORA-06531错误。在给嵌套表变量或者VARRAY变量赋值之前,必须首先初始化集合变量。如果没有初始化集合变量,会触发该异常。
(4)CURSOR_ALREADY_OPEN
该异常应用于ORA-06511错误。当在已打开游标上执行OPEN操作时,会触发该异常。
(5)INVALID_CURSOR
该异常应用于ORA-01001错误。当视图从未打开游标提取数据,或者关闭未打开游标时,会触发该异常。
(6)INVALID_NUMBER
该异常应用于ORA-01722错误。当内嵌SQL语句不能将字符转变成数字时,会触发该异常。
(7)LOGIN_DENIED
该异常应用于ORA-01017错误。当连接到Oracle数据库时,如果提供了不正确的用户名或者口令,会触发该异常。
(8)NO_DATA_FOUND
该异常应用于ORA-01403错误。当执行SELECT INTO未返回行,或者引用了未初始化的PL/SQL表元素时,会触发该异常。
(9)NOT_LOGGED_ON
该异常应用于ORA-01012错误。如果没有连接到Oracle数据库,当执行内嵌SQL语句时,会触发该异常。
(10)PROGRAM_ERROR
该异常应用于ORA-06501错误。如果出现该错误,则表示存在PL/SQL内部问题,在这种情况下需要重新安装数据字典视图和PL/SQL包。
(11)ROWTYPE_MISMATCH
该异常应用于ORA-016504错误。当执行赋值操作时,如果宿主变量和游标变量具有不兼容的返回类型,会触发该异常。
(12)SELF_IS_NULL
该异常应用于ORA-30625错误。当使用对象类型时,如果在NULL实例上调用成员方法,会触发该异常。
(13)STORAGE_ERROR
该异常应用于ORA-06500错误。当执行PL/SQL块时,如果超出内存空间或者内存被破坏,会触发该异常。
(14)SUBSCRIPT_BEYOND_COUNT
该异常应用于ORA-06533错误。当使用嵌套表或者VARRAY元素时,如果下标超出了嵌套表或者VARRAY元素的范围,会触发该异常。
(15)SUBSCRIPT_OUTSIDE_LIMIT
该异常应用于ORA-06532错误。当使用嵌套表或者VARRAY元素时,如果元素下标为负值,会触发该异常。
(16)SYS_INVALID_ROWID
该异常应用于ORA-01410错误。当将字符串转变为ROWID时,如果使用了无效字符串,会触发该异常。
(17)TIMEOUT_ON_RESOURCE
该异常应用于ORA-00051错误。当等待资源时如果出现超时错误,会触发该异常。
(18)TOO_MANY_ROWS
该异常应用于ORA-01422错误。当执行SELECT INTO语句时,如果返回超过一行,会触发该异常。
(19)VALUE_ERROR
该异常应用于ORA-06502错误。当执行赋值操作时,如果变量长度不足以容纳实际数据,会触发该异常。
(20)ZERO_DIVIDE
该异常应用于ORA-01476错误。如果用数字值除以0,会触发该异常。
1、 3.3预定义异常示例;
set serveroutput on
declare
var_empno number; --定义变量,存储雇员编号
var_ename varchar2(50); --定义变量,存储雇员名称
begin
select empno,ename into var_empno,var_ename
from scott.emp
where deptno=10; --检索部门编号为 10 的雇员信息
if sql%found then --若检索成功,则输出雇员信息
dbms_output.put_line('雇员编号:'||var_empno||';雇员名称'||var_ename);
end if;
exception --捕获异常
when too_many_rows then --若 SELECT INTO 语句的返回记录超过一行
dbms_output.put_line('返回记录超过一行');
when no_data_found then --若 SELECT INTO 语句的返回记录为 0 行
dbms_output.put_line('无数据记录');
end;
/
输出结果为:
在上面的例子中,由于部门编号为10的员工记录数大于1,所以SELECT INTO语句的返回行数就要超过一行,由于Oracle系统内部规定不允许该语句的返回行数超过一行,所以必然会引发异常,即引发too_many_rows系统预定义异常。
1.4 自定义异常
1.4.1 自定义异常介绍
Oracle系统内部的预定义异常仅仅20个左右,而实际程序运行过程中可能会产生几千种异常情况,为此Oracle经常使用错误编号和相关描述输出异常信息。另外,程序设计人员可以根据实际的业务需求定义一些特殊异常,这样Oracle的自定义异常就可以分为错误编号异常和业务逻辑异常两种。
1.4.2 错误编号异常
错误编号异常是指在Oracle系统发生错误时,系统会显示错误号和相关描述信息的异常。虽然直接使用错误编号也可以完成异常处理,但错误编号较为抽象,不易于用户理解和记忆,对于这种异常,首先在PL/SQL块的声明部分(DECLARE部分)使用EXCEPTION类型定义一个异常变量名,然后使用语句PRAGMA EXCEPTION_INIT为“错误编号”关联“这个异常变量名”,接下来就可以像对待系统预定义异常一样处理了。
下面通过一个具体的实例来演示如何为Oracle系统的“错误编号”做自定义异常处理。首先我们向dept表中插入一条部门编号为10的记录(部门编号10已经存在于dept表中,并且部门编号为dept表的唯一主键),然后执行INSERT语句,得到如图所示的运行结果。
从上图所示的运行结果中可以看到,程序执行中断而崩溃掉了,并显示错误信息为“ORA-00001”—即错误编号为“00001”,那么对于Oracle捕获到的这个异常可以通过如下的示例来解决。
set serveroutput on
declare
primary_iterant exception; --定义一个异常变量
pragma exception_init(primary_iterant,-00001); --关联错误号和异常变量名
begin
/*向 dept 表中插入一条与已有主键值重复的记录,以便引发异常*/
insert into scott.dept values(10,'软件开发部','深圳');
exception
when primary_iterant then --若 Oracle 捕获到的异常为-0001 异常
dbms_output.put_line('主键不允许重复!'); --输出异常描述信息
end;
/
输出结果为:
通过运行结果可以看到,使用异常处理机制,可以防止Oracle系统因引发异常而导致程序崩溃,使程序有机会自动纠正错误,而且自定义异常容易理解和记忆,方便用户的使用。
1.4.3 业务逻辑异常
在实际的应用中,程序开发人员可以根据具体的业务逻辑规则自定义一个异常。这样,当用户操作违反业务逻辑规则时,就引发一个自定义异常,从而中断程序的正常执行并转到自定义的异常处理部分。
无论是预定义异常,还是错误编号异常,都是由Oracle系统判断的错误,但业务逻辑异常是Oracle系统本身无法知道的,这样就需要有一个引发异常的机制,引发业务逻辑异常通常使用RAISE语句来实现,当引发一个异常时,控制就会转到EXCEPTION异常处理部分执行异常处理语句。
业务逻辑异常首先要在DECLARE部分使用EXCEPTION类型声明一个异常变量,然后在BEGIN部分根据一定的业务逻辑规则执行RAISE语句(在RAISE关键字后面跟着异常变量名),最后在EXCEPTION部分编写异常处理语句。
示例
set serveroutput on
declare
--声明一个 exception 类型的异常变量
null_exception exception;
--声明 rowtype 类型的变量 dept_row
dept_row scott.dept%rowtype;
begin
--给部门编号变量赋值
dept_row.deptno := 66;
--给部门名称变量赋值
dept_row.dname := '公关部';
--向 dept 表中插入一条记录
insert into scott.dept
values(dept_row.deptno,dept_row.dname,dept_row.loc);
--如果判断“loc”变量的值为 null
if dept_row.loc is null then
--引发 null 异常,程序转入 exception 部分
raise null_exception;
end if;
exception
--当 raise 引发的异常是 null_exception 时
when null_exception then
--输出异常提示信息
dbms_output.put_line('loc 字段的值不许为 null');
--回滚插入的数据记录
rollback;
end;
/
输出结果为: