(本文使用Oracle19c 和 scott表)
异常处理
异常处理是防范程序的执行错误或未预料到的事件
ORACLE异常以 ORA-xxxxx 形式呈现 (其中xxxxx为错误编号),使用 EXCEPTION 和异常处理 EXCEPTION HANDLER 来实现错误处理
注意异常处理不是解决编译错误的,因为编译错误发生在程序执行前
如果产生异常而没有处理时,程序就会自动终止整个程序运行
异常处理的格式
EXCEPTION
WHEN 异常名称1 THEN <处理异常1的代码>
WHEN 异常名称2 THEN <处理异常2的代码>
WHEN OTHERS THEN <处理其它异常的代码>
END;复制
异常的分类:
预定义异常
非预定义异常
用户自定义异常
预定义异常
ORACLE预定义的异常情况大约有24个。对这种异常情况的处理,无需在程序中定义,由ORACLE自动将其引发
处理方式是在PL/SQL块的异常处理部分,直接引用相应的异常情况名,并对其完成相应的异常错误处理即可
预定义异常表
错误号 | 异常错误信息名称 | 说明 |
---|---|---|
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 | SELECT INTO没有找到数据 |
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-6504 | Rowtype-mismatch | 宿主游标变量与 PL/SQL变量有不兼容行类型 |
ORA-6511 | CURSOR-already-OPEN | 试图打开一个已处于打开状态的游标 |
ORA-6530 | Access-INTO-null | 试图为null 对象的属性赋值 |
ORA-6531 | Collection-is-null | 试图将Exists 以外的集合( collection)方法应用于一个null pl/sql 表上或varray上 |
ORA-6532 | Subscript-outside-limit | 对嵌套或varray索引得引用超出声明范围以外 |
ORA-6533 | Subscript-beyond-count | 对嵌套或varray 索引得引用大于集合中元素的个数. |
例:更新指定员工工资,如工资小于1500,则加100(使用异常处理)
DECLARE
-- 声明变量保存员工号和工资
v_empno emp.empno%TYPE := &员工号;
v_sal emp.sal%TYPE;
BEGIN
-- 先查询工资
SELECT sal INTO v_sal FROM emp WHERE empno = v_empno;
-- if语句判断工资是否1500以内
IF v_sal<=1500 THEN
-- 如果是:修改工资+100
UPDATE emp SET sal = sal + 100 WHERE empno=v_empno;
DBMS_OUTPUT.PUT_LINE(v_empno||'员工工资已更新!');
COMMIT;
ELSE
-- 如果不是:只输出提示不做修改
DBMS_OUTPUT.PUT_LINE(v_empno||'员工工资已经超过1500!');
END IF;
-- 注意: 异常的写法
EXCEPTION
-- 先处理:预定义的NO_DATA_FOUND异常
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('数据库中没有编码为'||v_empno||'的员工');
-- 再处理:预定义的TOO_MANY_ROWS异常
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('程序运行错误!请使用游标');
-- 最后处理:其它可能会有的异常
WHEN OTHERS THEN
-- SQLCODE:错误码,SQLERRM:错误消息
DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;
复制
区别 NO_DATA_FOUND 和 %NOTFOUND:复制
SELECT … INTO 语句触发 NO_DATA_FOUND
当一个显式游标的WHERE子句未找到时触发%NOTFOUND
当UPDATE或DELETE 语句的WHERE 子句未找到时触发 SQL%NOTFOUND
在提取循环中要用 %NOTFOUND 或%FOUND 来确定循环的退出条件,不要用 NO_DATA_FOUND
非预定义异常
即其它标准的 ORACLE 异常。对这种异常情况的处理,需要用户在程序中定义,然后由ORACLE自动将其引发
步骤:
定义异常
使用EXCEPTION_INIT将定义的异常与标准的ORACLE错误关联
执行异常处理
例:删除指定部门的记录信息,以确保该部门没有员工 (使用异常处理)
DECLARE
v_deptno dept.deptno%TYPE := &部门号;
deptno_remaining EXCEPTION;
-- -2292 是违反一致性约束的错误代码
PRAGMA EXCEPTION_INIT(deptno_remaining, -2292);
BEGIN
-- 删除部门
DELETE FROM dept WHERE deptno = v_deptno;
COMMIT;
-- 异常处理
EXCEPTION
-- 处理非预定义异常的异常
WHEN deptno_remaining THEN
DBMS_OUTPUT.PUT_LINE('违反数据完整性约束!');
-- 处理其它可能会有的异常
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;
复制
用户自定义异常复制
程序执行过程中,出现开发人员认为的非正常情况。对这种异常情况的处理,需要用户在程序中定义,然后在程序中将其引发
做法是:通过使用 RAISE 语句来抛出异常,程序控制就转向到 EXCEPTION块异常错误部分,执行错误处理代码
步骤:
定义异常
使用RAISE抛出异常
执行异常处理
例:更新指定员工工资,如果更新失败,抛出异常
DECLARE
-- 声明员工号变量
v_empno emp.empno%TYPE :=&员工号;
-- 声明自定义的异常
no_result EXCEPTION;
BEGIN
-- 修改该员工的工资
UPDATE emp SET sal = sal+100 WHERE empno = v_empno;
-- 如果员工号不存在,修改操作失败
IF SQL%NOTFOUND THEN
-- 抛出自定义异常
RAISE no_result;
END IF;
-- 异常处理
EXCEPTION
-- 处理抛出的自定义异常
WHEN no_result THEN
DBMS_OUTPUT.PUT_LINE('你的数据更新语句失败了!');
-- 处理其它的异常
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END;
复制
复制
附:用户自定义异常还可以用 RAISE_APPLICATION_ERROR 的语法 重新定义异常错误消息
RAISE_APPLICATION_ERROR(错误码,错误消息,[keep_errors] );复制
说明:
错误码 是从 –20,000 到 –20,999 之间的参数
错误消息 是相应的提示信息(< 2048 字节),
keep_errors 为可选,如果keep_errors =TRUE , 则新错误将被添加到已经引发的错误列表中。如果keep_errors=FALSE(缺省),则新错误将替换当前的错误列表