Technology Exchange
免费咨询热线
400-090-9964
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 没有连接到ORACLE
ORA-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 found
Cause: 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;
/