即使是写得最好的PL/SQL程序也会遇到错误或未预料到的事件。一个优秀的程序都应该能够正确处理各种出错情况,并尽可能从错误中恢复。任何ORACLE错误(报告为ORA-xxxxx形式的Oracle错误号)、PL/SQL运行错误或用户定义条件(不一写是错误)都可以。当然了,PL/SQL编译错误不能通过PL/SQL异常处理来处理,因为这些错误发生在PL/SQL程序执行之前。
ORACLE 提供异常情况(EXCEPTION)和异常处理(EXCEPTION HANDLER)来实现错误处理。
1 .异常的概念
在设计或运行一个程序时,常出现这样或那样的错误。针对这类错误,采取相应的措施。这就是异常处理(EXCEPTION)
异常处理就是针对系统中发生的各种错误所采取的处理措施,也叫例外.
1、异常的定义格式:
BEGIN …; EXCEPTION WHEN 异常情况 1[ OR 异常情况 2 …] THEN …; WHEN 异常情况 3[ OR 异常情况 4 …] THEN …; WHEN OTHERS THEN …; END;
说明:
- 在块中开始一个出错处理部分必须以关键子EXCEPTION开始;
- 将WHEN OTHERS 子句放置在所有其他出错处理子句的后面。最多只能有一个WHEN OTHERS 子句;
- 可以在块中定义多个句柄(HANDLER),每个句柄包含一组语句;
- 当一个错误发生时,在离开块之前,PL/SQL只处理一个句柄
2、异常的类型
oracle预定义的异常
oracle预定义的异常,当这些异常情况发生时,系统可捕获异常并在程序中根据异常名称对程序进行异常处理。
Oracle非预定义的异常
是其他标准的Oracle错误。对这种异常情况的处理,需要用户在PL/SQL块的declare部分中定义,并在异常触发后对其处理。
用户自定义的异常
是由程序员根据逻辑规则定义的一些异常并对它进行处理。
注意:ORACLE预定义异常和非预定义异常都是由ORACLE进行判断错误
2.Oracle预定义的异常处理
ORACLE错误 |
对应的错误名 |
描述 |
ORA_00001 |
DUP_VAL_ON_INDEX |
试图通过一个重复值更新或插入一条语句,即违反了唯一性约束 |
ORA_00051 |
TIMEOUT_ON_RESOURCE |
在等待资源时发生超时现象,例如DDL操作加的排他表级锁 |
ORA_01001 |
INVALID_CURSOR |
非法的游标操作,例如游标在没有打开时执行FETCH操作 |
ORA_01522 |
INVALID_NUMBER |
字符串向数字转换失败,即有个无效字符 |
ORA_01012 |
NO_LOGGED_ON |
在没有连接到ORACLE之前,对数据库进行调用 |
ORA_01015 |
LOGIN_DENIED |
在连接中提供了无效的用户名/口令 |
ORA_01403 |
NO_DATA_FOUND |
执行SELECT语句未查询到数据 |
ORA_01410 |
SYS_INVALID_ROWID |
一个隐式的CHARTOROWID转换时,包含了无效的字符或格式 |
ORA_01422 |
TOO_MANY_ROWS |
未使用游标,SELECT语句返回了多行数据 |
ORA_06502 |
VALUE_ERROR |
由于长度类型不合适,出现数字、数据转换、字符串或限制型错误 |
ORA_01456 |
ZERO_DIVIDE |
被零除 |
ORA_06500 |
STORAGE_ERROR |
如果PL/SQL运行时内存被破坏或不够就会引发内部的PL/SQL错误 |
ORA_06501 |
PROGRAM_ERROR |
内部的PL/SQL错误 |
ORA_06504 |
ROWTYPE_MISMATCH |
游标变量和PL/SQL结果集之间数据类型不匹配 |
ORA_06511 |
CURSOR_ALREADY_OPEN |
试图打开一个已经打开的游标 |
ORA_06530 |
ACCESS_INTO_NULL |
参考了没有初始化的数据库对象、LOB或其他非集合的符合类型(即这些对象的值都是NULL) |
ORA_06531 |
COLLECTION_IS_NULL |
参考了没有初始化的嵌套表或VARRAY集合中的元素、或调用了没有初始化的集合方法 |
ORA_06532 |
SUBSCRIPT_OUTSIDE_LIMIT |
使用了一个下标比VARRAY边界值高的云素 |
ORA_06533 |
SUBSCRIPT_BEYOND_COUNT |
使用了一个比嵌套表或VARRAY初始化元素个数多的元素 |
注:红色是比较常用到的
对这种异常情况的处理,只需在PL/SQL块的异常处理部分,直接引用相应的异常情况名,并对其完成相应的异常错误处理即可。
例1:对异常DUP_VAL_ON_INDEX的捕获及处理.向产品表中插入一条记录,当主键值重复时,输出该记录的主键值已经存在.
BEGIN INSERT INTO products(productid,productname) values(&prodid,’&prodname’); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN dbms_output.put_line('该主键值已存在'); WHEN OTHERS THEN dbms_output.put_line(‘其他错误'); END;
例2:对异常NO_DATA_FOUND和TOO_MANY_ROWS的捕获及处理.
查询某类别的产品信息,如不存在则显示异常’没有找到该产品’,如查到多行则显示异常‘查找到多个产品’,如只查到一行则显示产品编号、名称、单价
DECLARE rec_prod products%rowtype; BEGIN SELECT * INTO rec_prod FROM products WHERE categoryid=&cateid; dbms_output.put_line('产品编号'||rec_prod.productid); dbms_output.put_line('产品名称'||rec_prod.productname); dbms_output.put_line('产品单价'||rec_prod.unitprice); EXCEPTION WHEN NO_DATA_FOUND THEN dbms_output.put_line('没有找到该产品'); WHEN TOO_MANY_ROWS THEN dbms_output.put_line('查找到多个产品'); END;
3 . 非预定义的Oracle异常
要使用非预定义的Oracle异常,则用户必须首先进行定义。步骤如下:
(1)在PL/SQL块的declare定义部分定义异常名:
<exception_name> EXCEPTION;
(2)使用pragma exception_init语句,将其定义好的异常名与标准的Oracle错误编号联系起来:
pragma exception_init(<exception_name >, <handle_code>);
(3)在PL/SQL块异常的处理部分对异常情况做出相应的处理。
例3:定义一个非预定义异常,对删除产品表中产品编号为1的产品的异常进行异常处理
DECLARE Fk_delete EXCEPTION; PRAGMA EXCEPTION_INIT (Fk_delete ,-2292); BEGIN DELETE products WHERE productid=1; EXCEPTION WHEN Fk_delete THEN dbms_output.put_line(‘在另一个表中引用了该记录’); WHEN others THEN dbms_output.put_line(‘其它错误’||sqlcode); END;
例4:向产品表中插入一条记录,当主键重复时,输出’该主键值已存在’, 当产品名为空时,输出’违反了非空约束
DECLARE Name_null EXCEPTION; PRAGMA exception_init(name_null, -1400); BEGIN INSERT INTO products(productid,productname) values(&prodid,’&prodname’); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN dbms_output.put_line('该主键值已存在'); WHEN Name_null THEN dbms_output.put_line('违反了非空约束'); END;
4. 用户自定义的异常
当与一个异常错误相关的错误出现时,就会隐含触发该异常错误。用户定义的异常错误是通过显式使用 RAISE 语句来触发。当引发一个异常错误时,控制就转向到 EXCEPTION块异常错误部分,执行错误处理代码。
用户自定义异常的处理步骤
例5:对产品表中单价小于等于3元的产品的打折状态更新为1.如果没有找到这样的产品则触发用户自定义异常,并提示”未找到相应产品”,否则显示更新了几条信息.
DECLARE prod_excep EXCEPTION; BEGIN UPDATE products SET Discontinued=‘1’ WHERE unitprice<=3; IF sql%notfound THEN RAISE prod_excep; ELSE dbms_output.put_line(‘更新了’||sql%rowcount||’条记录’); END IF; EXCEPTION WHEN prod_excep THEN dbms_output.put_line(‘未找到相应产品’); END;
5.三种异常的简单比较
异常 |
描 述 |
处理方向 |
系统预定义的异常 |
在PL/SQL中经常出现的25个系统定义错误 |
不必定义,允许服务器隐式地触发它们 |
用户自定义的异常 |
开发者认为是非正确的一个条件 |
必须在说明部分定义,并且显示地触发它们 |
非预定义的系统异常 |
任何其他(25个以外的)服务器产生的标准错误 |
必须在说明部分定义,并且允许服务器隐式地触发它们 |