例外分类
为了处理PL/SQL应用程序的各种错误,开发人员可以使用各种类型的例外。Oracle提供了预定义例外、非预定义例外和自定义例外等三种例外类型。
在PL/SQL块中捕捉并处理例外需要使用额外的处理部分来完成,例外处理部分是 关键字EXCEPTION开始的,语法如下所示:
EXCEPTION WHEN exception1 [OR exception2...] THEN statement1; statement2; ... [WHEN exception3 [OR exception4...] THEN statement1; statement2; ... [when OTHERS THEN statement1; statement2; ...示例:
DECLARE v_ename emp.ename%TYPE; BEGIN SELECT ename INTO v_ename FROM emp WHERE empno = &NO; dbms_output.put_line(‘雇员名:‘ || v_ename); EXCEPTION WHEN NO_DATA_FOUND THEN dbms_output.put_line(‘雇员号不存在,请核实雇员名‘); END;
处理预定义例外
预定义例外是指由PL/SQL所提供的系统例外。当PL/SQL应用程序违反了Oracle规则或系统限制时,则会隐含地触发一个内部例外。1、常用预定义例外
(1)ACCESS_INTO_NULL
该例外对应于ORA-06530错误。当开发对象类型应用时,在引用对象属性之前,必须首先初始化对象。
建立对象类型:
CREATE TYPE emp_type AS object( NAME VARCHAR2(10), sal NUMBER(6,2) );捕捉并处理该例外的示例如下:
DECLARE v_emp emp_type; --v_emp emp_type := emp_type(‘‘,0); BEGIN v_emp.name := ‘张三‘; v_emp.sal := 1000; EXCEPTION WHEN ACCESS_INTO_NULL THEN dbms_output.put_line(‘首先初始化emp‘); END;(2)CASE_NOT_FOUND
该例外对应于ORA-06592错误。当在PL/SQL块中编写CASE语句时,如果在WHEN子句中没有包含必须的条件分支,并且没有包含ELSE子句,就会隐含地触发CASE_NOT_FOUND例外。
DECLARE v_sal emp.sal%TYPE; BEGIN SELECT sal INTO v_sal FROM emp WHERE empno = &NO; CASE WHEN v_sal < 1000 THEN UPDATE emp SET sal = sal + 100 WHERE empno = &NO; WHEN v_sal < 2000 THEN UPDATE emp SET sal = sal + 150 WHERE empno = &NO; WHEN v_sal < 3000 THEN UPDATE emp SET sal = sal + 200 WHERE empno = &NO; END CASE; EXCEPTION WHEN CASE_NOT_FOUND THEN dbms_output.put_line(‘在CASE语句中缺少与‘ || v_sal || ‘相关的条件‘); END;(3)COLLECTION_IS_NULL
该例外对应于ORA-06531错误。在给集合元素(嵌套表或VARRAY类型)赋值前,必须首先初始化集合元素。如果没有初始化集合元素,则会隐含地触发COLLECTION_IS_NULL例外。
DECLARE TYPE ename_table_type IS TABLE OF emp.ename%TYPE; ename_table ename_table_type; BEGIN SELECT ename INTO ename_table(1) FROM emp WHERE empno = &NO; dbms_output.put_line(‘雇员名:‘ || ename_table(1)); EXCEPTION WHEN COLLECTION_IS_NULL THEN dbms_output.put_line(‘必须使用构造方法初始化集合元素‘); END;(4)CURSOR_ALREADY_OPEN
该例外对应于ORA-06511错误。当重新打开已经打开的游标时,会隐含地触发例外CURSOR_ALREADY_OPEN。
DECLARE CURSOR cur_emp IS SELECT ename,sal FROM emp; BEGIN --打开游标 OPEN cur_emp; FOR emp_record IN cur_emp LOOP dbms_output.put_line(emp_record.ename); END LOOP; EXCEPTION WHEN CURSOR_ALREADY_OPEN THEN dbms_output.put_line(‘游标已经打开‘); END;(5)DUP_VAL_ON_INDEX
该例外对应于ORA-00001错误。当在惟一索引所对应的列上键入重复值时,会隐含地触发例外DUP_VAL_ON_INDEX。
BEGIN UPDATE dept SET deptno = 40 WHERE deptno = &NO; EXCEPTION WHEN DUP_VAL_ON_INDEX THEN dbms_output.put_line(‘在deptno列上不允许重复值‘); END;(6)INVALID_CURSOR
该例外对应于ORA-01001错误。当试图在不合法的游标上执行操作时,会隐含地触发例外INVALID_CURSOR。例如,如果要从未打开的游标提取数据,或者关闭未打开的游标,则会触发该例外。
DECLARE CURSOR cur_emp IS SELECT ename,sal FROM emp; TYPE emp_record_type IS RECORD( ename emp.ename%TYPE, sal emp.sal%TYPE ); emp_record emp_record_type; BEGIN --未打开游标 FETCH cur_emp INTO emp_record; --关闭游标 CLOSE cur_emp; EXCEPTION WHEN INVALID_CURSOR THEN dbms_output.put_line(‘请检查游标是否已经打开‘); END;(7)INVALID_NUMBER
该例外对应于ORA-01722错误。当内嵌SQL语句不能有效地将字符转变成数字时,会隐含地触发例外INVALID_NUMBER,例如数字值"100“被写成"1OO"。
BEGIN UPDATE emp SET sal = sal + ‘1OO‘; EXCEPTION WHEN INVALID_NUMBER THEN dbms_output.put_line(‘输入的数字值不正确‘); END;(8)NO_DATA_FOUND
该例外对应于ORA-01403错误。当执行SELECT INTO未返回行,或者引用了索引表未初始化的元素时,会隐含地触发例外NO_DATA_FOUND。
DECLARE v_sal emp.sal%TYPE; BEGIN SELECT sal INTO v_sal FROM emp WHERE LOWER(ename) = LOWER(‘&ename‘); dbms_output.put_line(‘工资:‘ || v_sal); EXCEPTION WHEN NO_DATA_FOUND THEN dbms_output.put_line(‘不存在该雇员‘); END;(9)TOO_MANY_ROWS
该例外对应于ORA-01422错误。当执行SELECT INTO语句时,如果返回超过一行,则会触发该例外。
DECLARE v_ename emp.ename%TYPE; BEGIN SELECT ename INTO v_ename FROM emp WHERE sal = 3600; EXCEPTION WHEN TOO_MANY_ROWS THEN dbms_output.put_line(‘返回多行‘); END;(10)ZERO_DIVIDE
该例外对应于ORA-01476错误。当运行PL/SQL块时,如果使用数字值除0,则会隐含地触发该例外。
DECLARE num1 INT := 100; num2 INT := 0; num3 NUMBER(6,2); BEGIN num3 := num1/num2; EXCEPTION WHEN ZERO_DIVIDE THEN dbms_output.put_line(‘除数不能为0‘); END;(11)SUBSCRIPT_BEYOND_COUNT
该例外对应于ORA-06533错误。当使用嵌套表或VARRAY元素时,如果元素下标超出了嵌套表或VARRAY元素的范围,则会隐含地触发SUBSCRIPT_BEYOND_COUNT例外。
DECLARE TYPE emp_array_type IS VARRAY(20) OF VARCHAR2(20); emp_array emp_array_type := emp_array_type(‘‘,‘‘); BEGIN dbms_output.put_line(emp_array(3)); EXCEPTION WHEN SUBSCRIPT_BEYOND_COUNT THEN dbms_output.put_line(‘超出下标范围‘); END;(12)SUBSCRIPT_OUTSIDE_LIMIT
该例外对应于ORA-06532错误。当使用嵌套表或VARRAY元素时,如果元素下标为负值,则会隐含地触发SUBSCRIPT_BEYOND_COUNT例外。
DECLARE TYPE emp_array_type IS VARRAY(20) OF VARCHAR2(20); emp_array emp_array_type := emp_array_type(‘‘,‘‘); BEGIN dbms_output.put_line(emp_array(-1)); EXCEPTION WHEN SUBSCRIPT_OUTSIDE_LIMIT THEN dbms_output.put_line(‘嵌套表或VARRAY下标不能为负‘); END;(13)VALUE_ERROR
该例外对应于ORA-06502错误。当在PL/SQL块中执行赋值操作时,如果变量长度不足以容纳实际数据,则会隐含地触发例外VALUE_ERROR。
DECLARE v_ename VARCHAR2(2); BEGIN SELECT ename INTO v_ename FROM emp WHERE empno = &NO; dbms_output.put_line(v_ename); EXCEPTION WHEN VALUE_ERROR THEN dbms_output.put_line(‘变量尺寸不足‘); END;
2、其他预定义例外
除了在PL/SQL块中经常需要处理常见的预定义例外之外,某些情况下你可能还需要使用其他预定义例外。下面简单介绍这些例外的作用。(1)LOGIN_DENIED
该例外对应于ORA-01017错误。当PL/SQL应用程序需要连接到Oracle数据库时,如果提供了不正确的用户名或口令,则会隐含地触发例外LOGIN_DENIED。
(2)NOT_LOGGED_ON
该例外对应于ORA-01012错误。如果应用程序没有连接到Oracle数据库,那么在执行PL/SQL块中访问数据库时会隐含地触发例外NOT_LOGGED_ON。
(3)PROGRAM_ERROR
该例外对应于ORA-06501错误。如果出现该错误,则表示存在PL/SQL内部问题,用户此时可能需要重新安装数据字典和PL/SQL系统包。
(4)ROWTYPE MISMATCH
该例外对应于ORA-06504错误。当执行赋值操作时,如果宿主游标变量和PL/SQL游标变量的返回类型不兼容,那么会隐含地触发例外ROWTYPE_MISMATCH。
(5)SELF_IS_NULL
该例外对应于ORA-30625错误。当使用对象类型时,如果在null实例上调用成员方法,则会隐含地触发例外SELF_IS_NULL。
(6)STORAGE_ERROR
该例外对应于ORA-06500错误。PL/SQL块运行时,如果超出内存空间或者内存被损坏,则会隐含地触发例外STORAGE_ERROR。
(7)SYS_INVALID_ROWID
该例外对应于ORA-01410错误。当将字符串转变为ROWID时,必须使用有效的字符串。如果使用了无效的字符串,会隐含地触发例外SYS_INVALID_ROWID。
(8)TIMEOUT_ON_RESOURCE
该例外对应于ORA-00051错误。当如果Oracle在等待资源时出现超时错误,则会隐含地触发例外TIMEOUT_ON_RESOURCE。