♣题目 部分
Oracle中的异常可以分为哪几类?
♣答案部分
异常(EXCEPTION)处理是用来处理正常执行过程中未预料到的事件,包括程序块的异常处理、预定义的错误和自定义错误。如果PL/SQL程序块一旦产生异常,而程序并没有指出如何处理,那么程序就会自动终止运行。
异常处理部分一般放在PL/SQL程序体的后半部分,结构如下所示:
EXCEPTION
WHEN first_exception THEN <code to handle first exception >
WHEN second_exception THEN <code to handle second exception >
WHEN OTHERS THEN <code to handle OTHERS exception >
对于异常处理,需要注意以下几点内容:
(1)异常处理可以按任意次序排列,但OTHERS必须放在最后。
(2)当程序出现异常时,程序立即暂停工作,跳转到EXCEPTION部分。使用WHEN … THEN来处理系统定义的异常。使用WHEN OTHERS THEN处理未定义的异常。
(3)当异常处理结束后,Oracle就将处理权交给调用者。结束PL/SQL块的运行。
Oracle将异常分为预定义异常、非预定义异常和自定义异常三种。
1、预定义异常(Predefined)
当PL/SQL应用程序违反了Oracle规定的限制时,就会隐含地触发一个内部异常,这就是预定义异常。预定义异常用于处理常见的Oracle错误,对这种异常情况的处理,无需在程序中定义,由Oracle自动将其触发。它们全部放在PL/SQL自带的标准包中,这样程序员就无需再次定义了。预定义异常大约有20多个,下表是一些常见的预定义异常:
表 5-3 Oracle预定义异常
错误号 |
异常错误信息名称 |
说明 |
ORA-01403 |
NO_DATA_FOUND |
SELECT INTO没有找到数据 |
ORA-01422 |
TOO_MANY_ROWS |
SELECT INTO返回多行 |
ORA-06501 |
PROGRAM_ERROR |
内部错误,需重新安装数据字典视图和PL/SQL包 |
ORA-06511 |
CURSOR_ALREADY_OPEN |
试图打开一个已存在的游标 |
ORA-06530 |
ACCESS_INTO_NULL |
试图为NULL对象的属性赋值 |
ORA-01012 |
NOT_LOGGED_ON |
没有连接到Oracle |
ORA-01001 |
INVALID_CURSOR |
试图使用一个无效的游标 |
ORA-00061 |
TRANSACTION_BACKED_OUT |
由于发生死锁事务被撤消 |
ORA-00051 |
TIMEOUT_ON_RESOURCE |
在等待资源时发生超时 |
ORA-00001 |
DUP_VAL_ON_INDEX |
试图破坏一个唯一性限制 |
ORA-01017 |
LOGIN_DENIED |
无效的用户名/口令 |
ORA-01476 |
ZERO_DIVIDE |
试图被零除 |
ORA-01722 |
INVALID_NUMBER |
转换一个数字失败 |
ORA-06500 |
STORAGE_ERROR |
内存不够或内存被破坏触发的内部错误 |
ORA-06502 |
VALUE_ERROR |
赋值操作,变量长度不足,触发该异常 |
ORA-06504 |
ROWTYPE_MISMATCH |
宿主游标变量与PL/SQL变量有不兼容行类型 |
ORA-06531 |
COLLECTION_IS_NULL |
试图给没有初始化的嵌套表变量或者VARRY变量赋值 |
ORA-06532 |
SUBSCRIPT_OUTSIDE_LIMIT |
对嵌套或VARRAY索引使用了负数 |
ORA-06533 |
SUBSCRIPT_BEYOND_COUNT |
对嵌套或VARRAY索引的引用大于集合中元素的个数 |
预定义异常的示例如下所示:
SYS@lhrdb> SET SERVEROUTPUT ON
SYS@lhrdb> DECLARE
2 V_ENAME SCOTT.EMP.ENAME%TYPE;
3 V_SAL SCOTT.EMP.SAL%TYPE;
4 V_INPUT SCOTT.EMP.SAL%TYPE := 100;
5 BEGIN
6 SELECT ENAME, SAL INTO V_ENAME, V_SAL FROM SCOTT.EMP WHERE SAL = V_INPUT;
7 DBMS_OUTPUT.PUT_LINE(V_ENAME || ',' || V_SAL);
8 EXCEPTION
9 WHEN NO_DATA_FOUND THEN
10 DBMS_OUTPUT.PUT_LINE('没找到任何数据');
11 WHEN TOO_MANY_ROWS THEN
12 DBMS_OUTPUT.PUT_LINE('找到多行数据,建议使用游标');
13 WHEN OTHERS THEN
14 DBMS_OUTPUT.PUT_LINE('出错了!!');
15 DBMS_OUTPUT.PUT_LINE(SQLCODE || ',' || SQLERRM);
16
17 END;
18 /
没找到任何数据
PL/SQL procedure successfully completed.
2、非预定义异常(Non Predefined)
非预定义异常用于处理预定义异常不能处理的异常,即其它标准的Oracle错误。使用预定义异常只能处理系统预定义的20多个Oracle错误,而当使用PL/SQL开发应用程序时,可能会遇到其它的一些Oracle错误。例如,在PL/SQL块中执行DML语句时,违反了约束规定等等。在这样的情况下,就可以使用非预定义异常来处理。
Oracle提供了2个函数SQLCODE和SQLERRM用于返回错误信息:
l SQLCODE:返回错误代码
l SQLERRM:返回与错误代码关联的消息
这样就可以在错误日志表中记录程序在执行过程中发生的错误信息了。
非预定义异常的示例如下所示:
SYS@lhrdb> SET SERVEROUTPUT ON
SYS@lhrdb> DECLARE
2 FK_EXCEPTION EXCEPTION; --定义一个异常类型的变量
3 PRAGMA EXCEPTION_INIT(FK_EXCEPTION, -2292); --将该变量和指定的错误码绑定
4 V_ERROR_CODE NUMBER;
5 V_ERROR_MESSAGE VARCHAR2(255);
6 BEGIN
7 DELETE FROM SCOTT.DEPT WHERE DEPTNO = 20;
8 EXCEPTION
9 WHEN FK_EXCEPTION THEN
10 --可以用异常类型的名字进行异常的捕获
11 DBMS_OUTPUT.PUT_LINE('找到子记录,删除失败');
12 WHEN OTHERS THEN
13 ROLLBACK;
14 DBMS_OUTPUT.PUT_LINE(SQLCODE || ',' || SQLERRM);
15 END;
16 /
找到子记录,删除失败
PL/SQL procedure successfully completed.
3、自定义异常(User_define)
自定义异常用于处理与Oracle错误无关的其它情况。对这种异常情况的处理,需要用户在程序中定义,然后显式地在程序中将其触发。
预定义异常和自定义异常都是与Oracle错误相关的,并且出现的Oracle错误会隐含地触发相应的异常;而自定义异常与Oracle错误没有任何关联,它是由开发人员为特定情况所定义的异常。当与一个异常相关的错误出现时,就会隐含触发该异常。用户定义异常是通过显式使用RAISE语句来触发。当触发一个异常时,控制程序就转到异常块部分,执行错误处理代码。
自定义异常的示例如下所示:
SYS@lhrdb> SET SERVEROUTPUT ON
SYS@lhrdb> DECLARE
2 V_SAL SCOTT.EMP.SAL%TYPE;
3 V_ENAME SCOTT.EMP.ENAME%TYPE;
4 V_EMPNO SCOTT.EMP.EMPNO%TYPE := 6;
5 SALARY_EXCEPTION EXCEPTION; --定义异常的类型(名字)
6 BEGIN
7 SELECT ENAME, SAL INTO V_ENAME, V_SAL FROM SCOTT.EMP WHERE EMPNO = V_EMPNO;
8 IF V_SAL < 1500 THEN
9 V_SAL := V_SAL + 100;
10 DBMS_OUTPUT.PUT_LINE(V_ENAME || '涨工资后:' || V_SAL);
11 ELSE
12 RAISE SALARY_EXCEPTION; --抛出自定义的异常
13 END IF;
14 EXCEPTION
15 WHEN SALARY_EXCEPTION THEN
16 --捕获自定义的异常
17 DBMS_OUTPUT.PUT_LINE('薪金没有达到最低水平,不需要涨工资');
18 WHEN NO_DATA_FOUND THEN
19 DBMS_OUTPUT.PUT_LINE('没有找到' || V_EMPNO || '编码的员工');
20 WHEN OTHERS THEN
21 DBMS_OUTPUT.PUT_LINE(SQLCODE || ',' || SQLERRM);
22 END;
23 /
没有找到6编码的员工
PL/SQL procedure successfully completed.