如果没有异常,在程序中,应当检查每个命令的成功还是失败,使用异常,可以方便处理错误,而且异常处理程序与正常的事务逻辑分开,提高了可读性。
1、异常的优点
2、异常的分类
有两种类型的异常,一种为内部异常,一种为用户自定义异常,内部异常是执行期间返回到PL/SQL块的。用户自定义异常由开发者显示定义,在PL/SQL块中传递信息以控制对于应用的错误处理。
为每个ORACLE 错误都有一个号码并且在PL/SQL中异常通过名字处理,ORACLE 提供了预定义的内部异常。如SELECT INTO 语句不返回行时产生的ORACLE 异常NO_DATA_FOUND。最常用的异常列举如下:
exception | oracle error | sqlcode value | condition |
no_data_found | ora-01403 | +100 | select into 语句没有符合条件的记录返回 |
too_mang_rows | ora-01422 | -1422 | select into 语句符合条件的记录有多条返回 |
dup_val_on_index | ora-00001 | -1 | 对于数据库表中的某一列,该列已经被限制为唯一索引,程序试图存储两个重复的值 |
value_error | ora-06502 | -6502 | 在转换字符类型,截取或长度受限时,会发生该异常,如一个字符分配给一个变量,而该变量声明的长度比该字符短,就会引发该异常 |
storage_error | ora-06500 | -6500 | 内存溢出 |
zero_divide | ora-01476 | -1476 | 除数为零 |
case_not_found | ora-06592 | -6530 | 对于选择case语句,没有与之相匹配的条件,同时,也没有else语句捕获其他的条件 |
cursor_already_open | ora-06511 | -6511 | 程序试图打开一个已经打开的游标 |
timeout_on_resource | ora-00051 | -51 | 系统在等待某一资源,时间超时 |
3、异常的抛出
由三种方式抛出异常
1. 通过PL/SQL运行时引擎
2. 使用RAISE语句
3. 调用RAISE_APPLICATION_ERROR存储过程
当数据库或PL/SQL在运行时发生错误时,一个异常被PL/SQL运行时引擎自动抛出。异常也可以通过RAISE语句抛出
RAISE exception_name;
显式抛出异常是程序员处理声明的异常的习惯用法,但RAISE不限于声明了的异常,它可以抛出任何任何异常。例如,你希望用TIMEOUT_ON_RESOURCE错误检测新的运行时异常处理器,你只需简单的在程序中使用下面的语句:
RAISE TIMEOUT_ON_RESOUCE;
RAISE_APPLICATION_ERROR内建函数用于抛出一个异常并给异常赋予一个错误号以及错误信息。自定义异常的缺省错误号是+1,缺省信 息是User_Defined_Exception。RAISE_APPLICATION_ERROR函数能够在pl/sql程序块的执行部分和异常部分 调用,显式抛出带特殊错误号的命名异常。
Raise_application_error(error_number,message[,true,false]))
错误号的范围是-20,000到-20,999。错误信息是文本字符串,最多为2048字节。TRUE和FALSE表示是添加(TRUE)进错误堆(ERROR STACK)还是覆盖(overwrite)错误堆(FALSE)。缺省情况下是FALSE。
如下代码所示:
IF product_not_found THEN
RAISE_APPLICATION_ERROR(-20123,'Invald product code' TRUE);
END IF;
4、异常的处理
PL/SQL程序块的异常部分包含了程序处理错误的代码,当异常被抛出时,一个异常陷阱就自动发生,程序控制离开执行部分转入异常部分,一旦程序进入异常部分就不能再回到同一块的执行部分。下面是异常部分的一般语法:
EXCEPTION
WHEN exception_name THEN
Code for handing exception_name
[WHEN another_exception THEN
Code for handing another_exception]
处理异常
END
如果要处理未命名的内部异常,必须使用OTHERS异常处理器或PRAGMA EXCEPTION_INIT 。PRAGMA由编译器控制,或者是对于编译器的注释。PRAGMA在编译时处理,而不是在运行时处理。EXCEPTION_INIT告诉编译器将异常名 与ORACLE错误码结合起来,这样可以通过名字引用任意的内部异常,并且可以通过名字为异常编写一适当的异常处理器。
在子程序中使用EXCEPTION_INIT的语法如下:
PRAGMA EXCEPTION_INIT(exception_name, -Oracle_error_number);
在该语法中,异常名是声明的异常,下例是其用法:
定义一个自定义异常A,然后用PRAGMA_EXCEPTION_INIT 将 异常 A 与 -1476 (除数为零)号系统内部异常进行帮定,当有出数为零的异常发生后,他就出发异常A。此时的A相当于 zero_divide 异常。
对于用户自定义异常,只能在PL/SQL块中的声明部分声明异常,异常的名字由EXCEPTION关键字引入:
reserved_loaned Exception
产生异常后,控制传给了子程序的异常部分,将异常转向各自异常控制块,必须在代码中使用如下的结构处理错误:
Exception
When exception1 then
Sequence of statements;
When exception2 then
Sequence of statements;
When others then
5、多个异常触发与捕捉的机制
看6个例子:
(1)普通的例子
输出:EXCEPTION A!
(2)
输出:OUT EXCEPTION B!
异常B触发之后,里层的代码都不会再执行,所以异常A不会触发。
(3)
输出:
IN EXCEPTION A!
OUT EXCEPTION B!
在异常A触发之后,异常B因为是在异常A触发的程序块的外层触发的,而异常A在内层也被处理了。所以B也会触发。也就是说,当异常出发之后,如果能及时处理,只能退出本层,不会退出上层。上一层后面的代码还会继续执行。但是,下一层的都不在执行。
(4)
输出:OUT EXCEPTION A!
这个有一点难理解。当异常A触发后,没有被马上处理,导致,在被处理之前,所有其他的代码都不会执行,后面的异常B不会被触发,直接到达了下一个Exception。继续处理异常A。
(5)
输出:OTHERS EXCEPTION!
异常C触发之后后边的代码不会执行。直接到Exception来处理异常。
(6)
输出:OTHERS EXCEPTION!
异常A触发之后,又出发了异常C而没有对异常C进行即时的处理。所以异常B不会触发,代码自动转到下面的Exception.
-------------------------------------------------------------------------------------------------------------
1、异常 的优点
如果没有异常 ,在程序中,应当检查每个命令的成功还是失败,如
BEGIN
SELECT ...
-- check for ’no data found’ error
SELECT ...
-- check for ’no data found’ error
SELECT ...
-- check for ’no data found’ error
这种实现的方法缺点在于错误处理没有与正常处理分开,可读性差,使用异常 ,可以方便处理错误,而且异常 处理程序与正常的事务逻辑分开,提高了可读性,如
BEGIN
SELECT ...
SELECT ...
SELECT ...
...
EXCEPTION
WHEN NO_DATA_FOUND THEN -- catches all ’no data found’ errors
2、异常 的分类
有两种类型的异常 ,一种为内部异常 ,一种为用户自定义异常 ,内部异常 是执行期间返回到PL/SQL块的ORACLE 错误或由PL/SQL代码的某操作引起的错误,如除数为零或内存溢出的情况。用户自定义异常 由开发者显示定义,在PL/SQL块中传递信息以控制对于应用的错误处理。
每当PL/SQL违背了ORACLE 原则或超越了系统依赖的原则就会隐式的产生内部异常 。因为每个ORACLE 错误都有一个号码并且在PL/SQL中异常 通过名字处理,ORACLE 提供了预定义的内部异常 。如SELECT INTO 语句不返回行时产生的ORACLE 异常 NO_DATA_FOUND。对于预定义异常 ,现将最常用的异常 列举如下:
exception oracle error sqlcode value condition
no_data_found ora-01403 +100 select into 语句没有符合条件的记录返回
too_many_rows ora-01422 -1422 select into 语句符合条件的记录有多条返回
dup_val_on_index ora-00001 -1 对于数据库表中的某一列,该列已经被限制为唯一索引,程序试图存储两个重复的值
value_error ora-06502 -6502 在转换字符类型,截取或长度受限时,会发生该异常 ,如一个字符分配给一个变量,而该变量声明的长度比该字符短,就会引发该异常
storage_error ora-06500 -6500 内存溢出
zero_divide ora-01476 -1476 除数为零
case_not_found ora-06592 -6530 对于选择case语句,没有与之相匹配的条件,同时,也没有else语句捕获其他的条件
cursor_already_open ora-06511 -6511 程序试图打开一个已经打开的游标
timeout_on_resource ora-00051 -51 系统在等待某一资源,时间超时
如果要处理未命名的内部异常 ,必须使用OTHERS异常 处理器或PRAGMA EXCEPTION_INIT 。PRAGMA由编译器控制,或者是对于编译器的注释。PRAGMA在编译时处理,而不是在运行时处理。EXCEPTION_INIT告诉编译器将异常 名与ORACLE 错误码结合起来,这样可以通过名字引用任意的内部异常 ,并且可以通过名字为异常 编写一适当的异常 处理器。
在子程序中使用EXCEPTION_INIT的语法如下:
PRAGMA EXCEPTION_INIT(exception_name, -Oracle _error_number);
在该语法中,异常 名是声明的异常 ,下例是其用法:
DECLARE
deadlock_detected EXCEPTION;
PRAGMA EXCEPTION_INIT(deadlock_detected, -60);
BEGIN
... -- Some operation that causes an ORA-00060 error
EXCEPTION
WHEN deadlock_detected THEN
-- handle the error
END;
对于用户自定义异常 ,只能在PL/SQL块中的声明部分声明异常 ,异常 的名字由EXCEPTION关键字引入:
reserved_loaned Exception
产生异常 后,控制传给了子程序的异常 部分,将异常 转向各自异常 控制块,必须在代码中使用如下的结构处理错误:
Exception
When exception1 then
Sequence of statements;
When exception2 then
Sequence of statements;
When others then
3、异常 的抛出
由三种方式抛出异常
1. 通过PL/SQL运行时引擎
2. 使用RAISE语句
3. 调用RAISE_APPLICATION_ERROR存储过程
当数据库或PL/SQL在运行时发生错误时,一个异常 被PL/SQL运行时引擎自动抛出。异常 也可以通过RAISE语句抛出
RAISE exception_name;
显式抛出异常 是程序员处理声明的异常 的习惯用法,但RAISE不限于声明了的异常 ,它可以抛出任何任何异常 。例如,你希望用TIMEOUT_ON_RESOURCE错误检测新的运行时异常 处理器,你只需简单的在程序中使用下面的语句:
RAISE TIMEOUT_ON_RESOUCE;
比如下面一个订单输入的例子,若当订单小于库存数量,则抛出异常 ,并且捕获该异常 ,处理异常
DECLARE
inventory_too_low EXCEPTION;
---其他声明语句
BEGIN
IF order_rec.qty>inventory_rec.qty THEN
RAISE inventory_too_low;
END IF
EXCEPTION
WHEN inventory_too_low THEN
order_rec.staus:='backordered';
END;
RAISE_APPLICATION_ERROR内建函数用于抛出一个异常 并给异常 赋予一个错误号以及错误信息。自定义异常 的缺省错误号是+1,缺省信息是User_Defined_Exception。RAISE_APPLICATION_ERROR函数能够在pl/sql程序块的执行部分和异常 部分调用,显式抛出带特殊错误号的命名异常 。 Raise_application_error(error_number,message[,true,false]))
错误号的范围是-20,000到-20,999。错误信息是文本字符串,最多为2048字节。TRUE和FALSE表示是添加(TRUE)进错误堆(ERROR STACK)还是覆盖(overwrite)错误堆(FALSE)。缺省情况下是FALSE。
如下代码所示:
IF product_not_found THEN
RAISE_APPLICATION_ERROR(-20123,'Invald product code' TRUE);
END IF;
4、异常 的处理
PL/SQL程序块的异常 部分包含了程序处理错误的代码,当异常 被抛出时,一个异常 陷阱就自动发生,程序控制离开执行部分转入异常 部分,一旦程序进入异常 部分就不能再回到同一块的执行部分。下面是异常 部分的一般语法:
EXCEPTION
WHEN exception_name THEN
Code for handing exception_name
[WHEN another_exception THEN
Code for handing another_exception]
[WHEN others THEN
code for handing any other exception.]
用户必须在独立的WHEN子串中为每个异常 设计异常 处理代码,WHEN OTHERS子串必须放置在最后面作为缺省处理器处理没有显式处理的异常 。当异常 发生时,控制转到异常 部分,ORACLE 查找当前异常 相应的WHEN..THEN语句,捕捉异常 ,THEN之后的代码被执行,如果错误陷阱代码只是退出相应的嵌套块,那么程序将继续执行内部块END后面的语句。如果没有找到相应的异常 陷阱,那么将执行WHEN OTHERS。在异常 部分WHEN 子串没有数量限制。
EXCEPTION
WHEN inventory_too_low THEN
order_rec.staus:='backordered';
replenish_inventory(inventory_nbr=>
inventory_rec.sku,min_amount=>order_rec.qty-inventory_rec.qty);
WHEN discontinued_item THEN
--code for discontinued_item processing
WHEN zero_divide THEN
--code for zero_divide
WHEN OTHERS THEN
--code for any other exception
END;
当异常 抛出后,控制无条件转到异常 部分,这就意味着控制不能回到异常 发生的位置,当异常 被处理和解决后,控制返回到上一层执行部分的下一条语句。
BEGIN
DECLARE
bad_credit exception;
BEGIN
RAISE bad_credit;
--发生异常 ,控制转向;
EXCEPTION
WHEN bad_credit THEN
dbms_output.put_line('bad_credit');
END;
--bad_credit异常 处理后,控制转到这里
EXCEPTION
WHEN OTHERS THEN
--控制不会从bad_credit异常 转到这里
--因为bad_credit已被处理
END;
当异常 发生时,在块的内部没有该异常 处理器时,控制将转到或传播到上一层块的异常 处理部分。
BEGIN
DECLARE ---内部块开始
bad_credit exception;
BEGIN
RAISE bad_credit;
--发生异常 ,控制转向;
EXCEPTION
WHEN ZERO_DIVIDE THEN --不能处理bad_credite异常
dbms_output.put_line('divide by zero error');
END --结束内部块
--控制不能到达这里,因为异常 没有解决;
--异常 部分
EXCEPTION
WHEN OTHERS THEN
--由于bad_credit没有解决,控制将转到这里
END;
5、异常 的传播
没有处理的异常 将沿检测异常 调用程序传播到外面,当异常 被处理并解决或到达程序最外层传播停止。在声明部分抛出的异常 将控制转到上一层的异常 部分。
BEGIN
executable statements
BEGIN
today DATE:='SYADATE'; --ERRROR
BEGIN --内部块开始
dbms_output.put_line('this line will not execute');
EXCEPTION
WHEN OTHERS THEN
--异常 不会在这里处理
END;--内部块结束
EXCEPTION
WHEN OTHERS THEN
处理异常
END
--------------------------------------------------------------------------
1.简介
异常 处理块中包含了与异常 相关的错误发生以及当错误发生时要进行执行和处理的代码。异常 部分的语法一般如下:
异常 名 |
异常 标题 |
异常 号 |
ACCESS_INTO_NULL |
ORA-06530 |
-6530 |
CASE_NOT_FOUND |
ORA-06592 |
-6592 |
COLLECTION_IS_NULL |
ORA-06531 |
-6531 |
CURSOR_ALREADY_OPEN |
ORA-06511 |
-6511 |
DUP_VAL_ON_INDEX |
ORA-00001 |
-1 |
INVALID_CURSOR |
ORA-01001 |
-1001 |
INVALID_NUMBER |
ORA-01722 |
-1722 |
LOGIN_DENIED |
ORA-01017 |
-1017 |
NO_DATA_FOUND |
ORA-01403 |
-1403 |
NOT_LOGGED_ON |
ORA-01012 |
-1012 |
PROGRAM_ERROR |
ORA-06501 |
-6501 |
ROWTYPE_MISMATCH |
ORA-06504 |
-6504 |
SELF_IS_NULL |
ORA-30625 |
-30625 |
STORAGE_ERROR |
ORA-06500 |
-6500 |
SUBSCRIPT_BEYOND_COUNT |
ORA-06533 |
-6533 |
SUBSCRIPT_OUTSIDE_LIMIT |
ORA-06532 |
-6532 |
SYS_INVALID_ROWID |
ORA-01410 |
-1410 |
TIMEOUT_ON_RESOURCE |
ORA-00051 |
-51 |
TOO_MANY_ROWS |
ORA-01422 |
-1422 |
VALUE_ERROR |
ORA-06502 |
-6502 |
ZERO_DIVIDE |
ORA-01476 |
-1476 |
异常 名 |
说明 |
ACCESS_INTO_NULL |
Your program attempts to assign values to the attributes of an uninitialized (atomically null) object. |
CASE_NOT_FOUND |
one of the choices in the WHEN clauses of a ASE tatement is selected, and there is no ELSE clause. |
COLLECTION_IS_NULL |
Your program attempts to apply collection methods other than EXISTS to an uninitialized (atomically null) nested table or varray, or the program attempts to assign values to the elements of an uninitialized nested table or varray. |
CURSOR_ALREADY_OPEN |
Your program attempts to open an alrea*** open cursor. A cursor must be closed before it can be reopened. A cursor FOR loop automatically opens the cursor to which it refers. So, your program cannot open that cursor inside the loop. |
DUP_VAL_ON_INDEX |
Your program attempts to store duplicate values in a database column that is constrained by a unique index. |
INVALID_CURSOR |
Your program attempts an illegal cursor operation such as closing an unopened cursor. |
INVALID_NUMBER |
In a SQL statement, the conversion of a character st*** into a number fails because the st*** does not represent a valid number. (In procedural statements, VALUE_ERROR is raised.) This exception is also raised when the LIMIT-clause expression in a bulk FETCH statement does not evaluate to a positive number. |
LOGIN_DENIED |
Your program attempts to log on to Oracle with an invalid username and/or password. |
NO_DATA_FOUND |
A SELECT INTO statement returns no rows, or your program references a deleted element in a nested table or an uninitialized element in an index-by table. SQL aggregate functions such as AVG and SUM always return a value or a null. So, a SELECT INTO statement that calls an aggregate function never raises NO_DATA_FOUND. The FETCH statement is expected to return no rows eventually, so when that happens, no exception is raised. |
NOT_LOGGED_ON |
Your program issues a database call without being connected to Oracle . |
PROGRAM_ERROR |
PL/SQL has an internal problem. |
ROWTYPE_MISMATCH |
The host cursor variable and PL/SQL cursor variable involved in an assignment have incompatible return types. For example, when an open host cursor variable is passed to a stored subprogram, the return types of the actual and formal parameters must be compatible. |
SELF_IS_NULL |
Your program attempts to call a MEMBER method on a null instance. That is, the built-in parameter SELF (which is always the first parameter passed to a MEMBER method) is null. |
STORAGE_ERROR |
PL/SQL runs out of memory or memory has been corrupted. |
SUBSCRIPT_BEYOND_COUNT |
Your program references a nested table or varray element using an index number larger than the number of elements in the collection. |
SUBSCRIPT_OUTSIDE_LIMIT |
Your program references a nested table or varray element using an index number (-1 for example) that is outside the legal range. |
SYS_INVALID_ROWID |
The conversion of a character st*** into a universal rowid fails because the character st*** does not represent a valid rowid. |
TIMEOUT_ON_RESOURCE |
A time-out occurs while Oracle is waiting for a resource. |
TOO_MANY_ROWS |
A SELECT INTO statement returns more than one row. |
VALUE_ERROR |
An arithmetic, conversion, truncation, or size-constraint error occurs. For example, when your program selects a column value into a character variable, if the value is longer than the declared length of the variable, PL/SQL aborts the assignment and raises VALUE_ERROR. In procedural statements, VALUE_ERROR is raised if the conversion of a character st*** into a number fails. (In SQL statements, INVALID_NUMBER is raised.) |
ZERO_DIVIDE |
Your program attempts to divide a number by zero. |
3.自定义异常
异常 不一定必须是Oracle 返回的系统错误,用户可以在自己的应用程序中创建可触发及可处理的自定义异常 ,调用异常 处理需要使用RAISE语句。
异常 情态的传播指的是当在程序块的声明、执行、异常 部分分别出现异常 情态时,或在本块中没有相应的异常 处理器时会将这个异常 情态传播到哪里,会去激发那个块中的处理器。传播规则是这样的:当一个异常 情态是在块的执行部分引发的(最常见的),PL/SQL使用下面的规则确定激活哪个异常 处理器。
① 若当前块对该异常 情态设置了处理器,则执行它并成功完成该块的执行,然后控制转给包含块。
② 若当前块没有该处理器,则通过在包含块中引发它来传播异常 情态。然后对包含块执行PL/SQL的异常 操作。另外,无论是在声明部分引发了一个异常 情态,还是在异常 处理部分引发,则该异常 情态将立即传播给包含块。在包含块引用上述规则进行异常 情态的处理,即使在当前块设置了