在总结PL/SQL的基础知识时,说到了以下内容:
[declare]
-- declaration statements
i integer;
begin
-- executable statements
[exception]
-- exception statements
end;
对于其中的exception
没有进行详细的总结,那么exception
在Oracle又是怎么样的呢?出现了exception
又要如何处理呢?这篇文章就对Oracle中的exception进行详细的总结。
在Oracle中,异常分为以下两类:
在Oracle中预定义的异常如下表所示:
异常名称 | 错误代码 | 产生原因 |
---|---|---|
ACCESS_INTO_NULL | 06530 | 为一个未初始化对象的属性赋值 |
CASE_NOT_FOUND | 06592 | 在CASE过程中,WHEN后没有包含必要的条件分支并且没有ELSE子句 |
COLLECTION_IS_NULL | 06531 | 使用未初始化的集合元素 |
CURSOR_ALREADY_OPEN | 06511 | 游标已经打开 |
DUP_VAL_ON_INDEX | 00001 | 在具有唯一索引的列上插入一个重复值 |
INVALID_CURSOR | 01001 | 在无效的游标上进行操作 |
INVALID_NUMBER | 01722 | 在一个SQL语句中,由于字符串并不代表一个有效的数字,导致字符串向数字转换时会发生错误。(在过程化语句中,会抛出异常VALUE_ERROR。)当FETCH语句的LIMIT子句表达式后面不是一个正数时,这个异常也会被抛出。 |
LOGIN_DENIED | 01017 | 使用错误的用户名或密码登陆数据库 |
NO_DATA_FOUND | 01403 | SELECT INTO未返回行,或者程序引用了嵌套表中一个已经删除的元素,或者引用表中一个为初始化的元素 |
NOT_LOGGED_ON | 01012 | 没有连接到数据库 |
PROGRAM_ERROR | 06501 | PL/SQL内部错误 |
ROWTYPE_MISMATCH | 06504 | 赋值语句中使用的主游标变量和PL/SQL游标变量的类型不兼容 |
SELF_IS_NULL | 30625 | 调用一个未被实例化的对象的成员方法,内置的SELF参数总是指向这个对象,SELF会作为函数的第一个参数传递到这个成员方法,就好比C++中的this |
STORAGE_ERROR | 06500 | 内存溢出或者内存不足 |
SUBSCRIPT_BEYOND_COUNT | 06533 | 程序引用一个嵌套表或变长数组元素,但使用的下标索引超过嵌套表或变长数组元素总个数 |
SUBSCRIPT_OUTSIDE_LIMIT | 06532 | 程序引用一个嵌套表或变长数组,但使用的下标索引不在合法的范围内 |
SYS_INVALID_ROWID | 01410 | 从字符串向ROWID转换发生错误,因为字符串并不代表一个有效的ROWID |
TIMEOUT_ON_RESOURCE | 00051 | 当Oracle请求资源时,发生超时现象 |
TOO_MANY_ROWS | 01422 | SELECT INTO返回的行数太多 |
VALUE_ERROR | 06502 | 发生算术、转换、截位或长度约束错误。例如,当我们的程序把一个字段的值放到一个字符变量中时,如果值的长度大于变量的长度,PL/SQL就会终止赋值操作并抛出异常VALUE_ERROR。在过程化语句中,如果字符串向数字转换失败,异常VALUE_ERROR就会被抛出(在SQL语句中,异常INVALID_NUMBER会被抛出) |
ZERO_DIVIDE | 01476 | 程序尝试除以0 |
和其它语言一样,我们也可以定义我们满足我们自己要求的异常,自定义一样的语法格式如下:
PRAGMA EXCEPTION_INIT(exception_name, -Oracle_error_number);
定义了那么多异常,那么在程序中如何抛出异常呢?在Oracle中有以下三种方式触发异常:
RAISE
语句手工触发RAISE_APPLICATION_ERROR
手工触发下面的代码将演示Oracle自动触发异常:
-- Created on 2015-7-14 by JellyThink
declare
iA NUMBER(2) := 10;
begin
iA := iA / 0; -- Oracle自动触发异常
dbms_output.put_line(iA);
exception
when ZERO_DIVIDE then
dbms_output.put_line('Error Code:' || SQLCODE || ' ' || SQLERRM);
when others then
dbms_output.put_line('Others Exception');
end;
下面的代码将演示使用RAISE
语句手工触发异常:
-- Created on 2015-7-14 by JellyThink
declare
myException EXCEPTION;
begin
RAISE myException; -- RAISE手工触发异常
exception
when others then
dbms_output.put_line('Others Exception');
end;
下面的代码将演示使用RAISE_APPLICATION_ERROR
触发异常:
-- Created on 2015-7-14 by JellyThink
declare
myException EXCEPTION;
PRAGMA EXCEPTION(myException, -20009);
begin
RAISE_APPLICATION_ERROR(-20009, 'Exception'); -- 这里可以指定异常信息
exception
when myException then
dbms_output.put_line('myException Exception');
end;
先来看看下面这段代码:
-- Created on 2015-7-15 by JellyThink
declare
i Number;
begin
i := 10;
declare
begin
i := i / 0; -- 嵌套块中抛出异常
/*exception
when ZERO_DIVIDE then
dbms_output.put_line('Error');*/ -- 嵌套块不处理这个异常
end;
exception
-- 在这里处理子块抛出的异常
when ZERO_DIVIDE then
dbms_output.put_line('Error Code:' || SQLCODE || ' ' || SQLERRM);
when others then
dbms_output.put_line('Error Code:' || SQLCODE || ' ' || SQLERRM);
end;
在一个块中,只能有一个异常处理部分,在不同的块中,可以定义多个异常处理部分。父块中可以定义异常处理,子块中也可以定义自己的异常处理。当一个子块的异常发生时,如果在子块的异常处理部分没有进行处理,该异常会传递到父块中,如果父块中没有对应的异常处理部分,那么这个异常会继续向上传递。
异常的处理在一个程序中是必不可少的,也是你的代码走向完美的一个必要的部分。只有熟悉的掌握了异常处理,才能更好的服务于你的代码。想想我们现在系统中的存储过程,都没有异常处理,哦,谁写的,我保证不打死你。
2015年7与15日 于呼和浩特。