1、定义异常:PL/SQL在执行的过程中所产生的标识符,PL/SQL中的一个警告或错误都被称为异常。错误通常分为两类:编译时错误--一般是语法错误,由PL/SQL引擎发现并报告给用户,程序本身不能处理,因为还没运行。运行时错误--程序在运行时引发的错误,例如没返回结果与返回多个结果(只能返回一行结果的情况下)异常处理---通常是指处理运行时错误。 引发异常的一个重要原因是处理数据时发生错误。统计表明,SELECT语句、DML语句以及游标操作语句更容易引发异常。 编写PL/SQL块的主要目的是处理数据,而PL/SQL块在逻辑上与数据是分开的,程序员根本无法预料数据的变化。 例如,要查询部门10的员工,程序员根本不知道这个部门中有没有员工, 有一个还是有多个员工。 所以在编写程序时,程序员应该考虑各种可能出现的异常,在程序中编写这些异常的处理代码,这样的程序才能经受各种错误的考验。
2、预定义异常预定义异常:Oracle把一些常见的错误定义为有名字的异常,这就是预定义的异常。【有异常代码,异常名称,异常消息】 Oracle有许多预定义的异常,在进行处理时不需要再定义,只需要编写相应的异常处理程序即可。 当PL/SQL块执行发生错误时,数据库服务器将自动抛出相应的异常,并执行编写的异常处理程序。 预定义说明的 oracle 异常 ORACLE 错误 异常名 说明ORA-0001 Dup_val_on_index 试图破坏一个唯一性限制ORA-0051 Timeout-on-resource 在等待资源时发生超时ORA-0061 Transaction-backed-out 由于发生死锁事务被撤消.ORA-1001 Invalid-cursor 试图使用一个无效的游标ORA-1012 Not-logged-on 没有连接到ORACLEORA-1017 Login-denied 无效的用户名/口令ORA-1403 No_data_found 没有找到数据ORA-1422 Too-many-rows select into 返回多行ORA-1476 Zero-divide 试图被零除ORA-1722 Invalid-number 转换一个数字失败ORA-6500 Storage-error 内存不够引发的内部错误ORA-6501 Program-error 内部错误ORA-6502 Value-error 转换或截断错误ORA-6511 Cursor-already-open 试图打开一个已存在的 游标ORA-6530 Access-into-null 试图为null 对象的属性赋值
3、非预定义异常非预定义异常:在PL/SQL中还有一类会经常遇到的错误,每个错误都有相应的错误代码和错误原因,但是由于Oracle没有为这样的错误定义一个名称,因而不能直接进行异常处理。在一般情况下,只能在PL/SQL块执行出错时查看其出错信息。对于这类非预定义的异常,由于它也被自动抛出的,因而只需要定义一个异常,把这个异常的名称与错误的代码关联起来,然后就可以像处理预定义异常那样处理这样的异常了。 在抛出ORACLE预定义异常之前先抛出自己定义的非预定义异常,可以大大节约异常定位的时间,比如说很多情况都最终导致同一种预定义异常情况下。
4、用户自定义异常用户自定义异常:程序员可以把一些特定的状态定义为异常。这样的异常一般由程序员自己决定,在一定的条件下抛出,然后利用PL/SQL的异常机制进行处理。对于用户自定义的异常,有两种处理方法。 第一种方法是先定义一个异常,并在适当的时候抛出,然后在PL/SQL块的异常处理部分进行处理。 用户自定义的异常一般在一定的条件下抛出,于是这个条件就成为引发这个异常的原因。 第二种方法是向调用者返回一个自定义的错误代码和一条错误信息。 自定义错误代码的范围是-20000到-20999,是oracle为自定义错误保留的 在处理用户自定义的异常时,也可以使用函数SQLCODE和SQLERRM,这两个函数分别用于返回指定的错误代码和错误信息。
5、系统预定义异常例子DECLARE v_ename emp1.ename%TYPE; v_sal emp1.sal%TYPE;BEGIN SELECT ename, sal INTO v_ename, v_sal FROM emp1; WHERE empno=9100; --不存在的员工编号,会返回NO_DATA_FOUND异常 DBMS_OUTPUT.PUT_LINE('ename is '||v_ename||'.'); DBMS_OUTPUT.PUT_LINE('salary is '||v_sal||'.');EXCEPTION WHEN NO_DATA_FOUND THEN --无返回数据 DBMS_OUTPUT.PUT_LINE('data not found.'); WHEN TOO_MANY_ROWS THEN --返回了多行数据 DBMS_OUTPUT.PUT_LINE('exact fetch returns more than requested number of rows.'); WHEN OTHERS THEN --其他异常 DBMS_OUTPUT.PUT_LINE('others exception.');END; OTHERS异常处理器能截获所有未经处理的异常,所以,OTHERS是最后定义的异常处理器。
6、非预定义异常例子DECLARE emp_e EXCEPTION; PRAGMA EXCEPTION_INIT (emp_e, -2292); --EXCEPTION_INIT将异常名和一个系统错误代码联系起来 v_deptno dept.deptno%TYPE := &p_deptno;BEGIN DELETE FROM dept WHERE deptno = v_deptno; --COMMIT;EXCEPTION WHEN emp_e THEN DBMS_OUTPUT.PUT_LINE ('Cannot remove dept ' || TO_CHAR(v_deptno) || '. Employees exist. ');END; ORA-02292: integrity constraint (string.string) violated - child record foundCause: attempted to delete a parent key value that had a foreign key dependency.Action: delete dependencies first then parent or disable constraint.8
7、使用错误函数(SQLCODE和SQLERRM)例一、DECLARE d_excep EXCEPTION; PRAGMA EXCEPTION_INIT (d_excep, -2292);BEGIN DELETE FROM dept WHERE deptno=30;EXCEPTION WHEN d_excep THEN DBMS_OUTPUT.PUT_LINE(SQLERRM);END;/例二、先建立一个保存错误信息的表CREATE TABLE u_err_table( username VARCHAR2(20), errdate DATE, errcode NUMBER, errm VARCHAR2(100) ); 执行以下语句块,可能引发的异常交给OTHERS处理DECLARE v_sqlcode NUMBER; v_sqlerrm VARCHAR2(100); v_deptno dept.deptno%TYPE := &deptno;BEGIN DELETE FROM dept WHERE deptno = v_deptno;EXCEPTION WHEN OTHERS THEN v_sqlcode := SQLCODE; v_sqlerrm := SQLERRM; INSERT INTO u_err_table VALUES (USER, SYSDATE, v_sqlcode, v_sqlerrm);END;/查看错误记录表:SELECT * FROM u_err_table;
8、用户自定义异常例子在上面错误函数的例子里,如果输入一个不存在的部门号,程序并不会产生异常,就要用到用户自定义的异常来实现。DECLARE e_err_deptno EXCEPTION; --定义异常 v_deptno dept.deptno%TYPE := &deptno;BEGIN DELETE FROM dept WHERE deptno = v_deptno; IF SQL%NOTFOUND THEN RAISE e_err_deptno; --抛出异常 END IF;EXCEPTION WHEN e_err_deptno THEN --处理异常 DBMS_OUTPUT.PUT_LINE('invalid deptno.');END; 可以使用RAISE_APPLICATION_ERROR返回自定义的错误消息,看起来跟ORACLE的标准错误一样。DECLARE v_sqlcode NUMBER; v_sqlerrm VARCHAR2(100); v_deptno dept.deptno%TYPE := &deptno;BEGIN DELETE FROM dept WHERE deptno = v_deptno; IF SQL%NOTFOUND THEN RAISE_APPLICATION_ERROR(-20001,'deptno not exists.'); END IF;EXCEPTION WHEN OTHERS THEN v_sqlcode := SQLCODE; v_sqlerrm := SQLERRM; DBMS_OUTPUT.PUT_LINE(v_sqlcode||': '||v_sqlerrm);END; 自定义异常的例子:DECLARE v_deptno dept1.deptno%TYPE := &deptno; dd_exc EXCEPTION;BEGIN DELETE FROM dept1 WHERE deptno=v_deptno; IF SQL%NOTFOUND THEN RAISE dd_exc; ELSE dbms_output.put_line(SQL%ROWCOUNT||' rows deleted.'); END IF;EXCEPTION WHEN dd_exc THEN dbms_output.put_line('no rows deleted.');END;/