Oracle异常处理

为了提高应用程序的健壮性 , 开发人员必须考虑程序可能出现的各种错误 , 并进行相应的处理 .
Oracle 中异常分为预定义例外 , 非预定义例外和自定义例外三种 .
## 处理预定义异常
预定义异常是指由 PL/SQL 所提供的系统异常 . PL/SQL 应用程序违反了 Oralce 规则或系统限制时 , 则会隐含的触发一个内部异常 .
# PL/SQL 为开发人员提供了二十多个预定义异常 :
 
1.ACCESS_INTO_NULL
该异常对应于 ORA-06530 错误 . 当开发对象类型应用时 , 如果没有初始化对象 , 直接为对象属性赋值 , 该异常触发 .
DECLARE
  emp emp_type;
BEGIN
  emp.name := 'SCOTT';
EXCEPTION
  WHEN ACCESS_INTO_NULL THEN
    DBMS_OUTPUT.PUT_LINE(' 首先初始化对象 emp');
END;
/
2.CASE_NOT_FOUND
对应于 ORA-06592 错误 . CASE 语句时 , 如果 WHEN 子句没有包含必须的条件分支 , 并且没有仓含 ELSE 子句 , 被触发 .
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 类型 ) 赋值前 , 必须首先寝化集合元素 . 否则触发该异常 .
DECLARE
  TYPE ename_table_type IS TABLE OF emp.ename%TYPE;
  ename_table ename_table_type;
BEGIN
  SELECT ename INTO ename_table(2) FROM emp WHERE empno = &no;
  DBMS_OUTPUT.PUT_LINE(' 雇员名 : ' || ename_table(2));
EXCEPTION
  WHEN COLLECTION_IS_NULL THEN
    DBMS_OUTPUT.PUT_LINE(' 必须初始化集合元素 .');
END;
/
4.CURSOR_ALREADY_OPEN
对应于 ORA-06511 错误 . 当重新打开已经打开的游标时 , 会隐含地触发该异常 .
DECLARE
  CURSOR emp_cursor IS
    SELECT ename, sal FROM emp;
BEGIN
  OPEN emp_cursor;
  FOR emp_record IN emp_cursor 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 错误 , 当在惟一索引所对应的列上键入重复值时触发 .
BEGIN
  UPDATE dept SET deptno = &new_no WHERE deptno = &old_no;
EXCEPTION
  WHEN DUP_VAL_ON_INDEX THEN
    DBMS_OUTPUT.PUT_LINE(' deptno 列上不能出现重复值 .');
END;
/
6.INVALID_CURSOR
对应于 ORA-01001 错误 . 当试图在不合法的游标上执行操作时触发 . 如从未打开的游标取数据 , 关闭未打开的游标等 .
DECLARE
  CURSOR emp_cursor IS
    SELECT ename, sal FROM emp;
  emp_record emp_cursor%ROWTYPE;
BEGIN
  FETCH emp_cursor
    INTO emp_record;
  CLOSE emp_cursor;
EXCEPTION
  WHEN INVALID_CURSOR THEN
    DBMS_OUTPUT.PUT_LINE(' 请检查游标是否已经打开 .');
END;
/
7.INVALID_NUMBER
对应于 ORA-0 1722 错误 . 当内嵌 SQL 语句不能有效地将字符转变成数字时触发 . 如数值 100 被写成 "1oo".
BEGIN
  UPDATE emp SET sal = sal + '100';
EXCEPTION
  WHEN INVALID_NUMBER THEN
    DBMS_OUTPUT.PUT_LINE(' 输入的数字不正确 .');
END;
/
8.NO_DATA_FOUND
对应于 ORA-0 1403 错误 . 当执行 SELECT INOT 未返回行 , 或引用了索引表未初始化元素时触发 .
DECLARE
  v_sal emp.sal%TYPE;
BEGIN
  SELECT sal INTO v_sal FROM emp WHERE lower(ename) = lower('&name');
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE(' 不存在该雇员 .');
END;
/
9.TOO_MANY_ROWS
对应于 ORA-0 1422 错误 , 当执行 SELECT INTO 语句时 , 如果返回超过一行触发该异常 .
DECLARE
  v_ename emp.ename%TYPE;
BEGIN
  SELECT ename INTO v_ename FROM emp WHERE sal = &sal;
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
  num2 := num1 / num2;
EXCEPTION
  WHEN ZERO_DIVIDE THEN
    DBMS_OUTPUT.PUT_LINE(' 分母不能为 0.');
END;
/
11.SUBSCRIPT_BEYOND_COUNT
对应于 ORA-06533 错误 . 当使用嵌套表或 VARRAY 元素时 , 如果下标越界触发该异常 .
DECLARE
  TYPE emp_array_type IS VARRAY(20) OF VARCHAR2(10);
  emp_array emp_array_type;
BEGIN
  emp_array := emp_array_type('SCOTT', 'MARY');
  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 元素时 , 如果下标为负值触发该异常 .
DECLARE
  TYPE emp_array_type IS varray(20) OF VARCHAR2(10);
  emp_array emp_array_type;
BEGIN
  emp_array := emp_array_type('SCOTT', 'MARY');
  DBMS_OUTPUT.PUT_LINE(emp_array(-1));
EXCEPTION
  WHEN SUBSCRIPT_OUTSIDE_LIMIT THEN
    DBMS_OUTPUT.PUT_LINE(' 下标不能是负数 .');
END;
/
13. VALUE_ERROR
对应于 ORA-06502 错误 . 当在 PL/SQL 块中执行赋值操作时 , 如果变量长度不足则触发该异常 .
DECLARE
  v_ename VARCHAR2(5);
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;
/
## 其它预定义异常
 
1.LONIN_DENIED
对应于 ORA-01017 错误 . PL/SQL 应用程序要连接 Oracle 数据库时 , 如果密码错误则触发该异常 .
2.NOT_LOGGED_ON
对应于 ORA-0 1012 错误 . 如果程序没连接 Oracle 数据库 , 那么执行 PL/SQL 访问数据库时触发该异常 .
3.PROGRAM_ERROR
对应于 ORA-06501. 如果出现该错误 , 则表示 PL/SQL 内部问题 , 用户可能需要重新安装数据字典和 PL/SQL 系统包 .
4.ROWTYPE MISMATCH
对应于 ORA-06504 错误 . 赋值时 , 宿主游标变量和 PL/SQL 游标变量的返回类型不兼容时触发该异常 .
5.SELF_IF_NULL
对应于 ORA- 3062 5. 当使用对象类型时 , 如果在 NULL 实例上调用成员方法则触发该异常 .
6.STORAGE_ERROR
对应于 ORA-06500 错误 .PL/SQL 块运行时 , 如果走出内在空间或内在被损坏则触发该异常 .
7.SYS_INVALID_ROWID
对应于 ORA-0 1410 错误 . 当将字符串转变为 ROWID , 如果使用了无效的字符串则触发该异常 .
8.TIMEOUT_ON_RESOURCE
对应于 ORA-00051 错误 .Oracle 在等待资源超出现超时错误时触发该异常 .
## 非预定义异常
使用预定义异常 , 只能处理 21 Oracle 错误 . 而当使用 PL/SQL 开发应用程序时 , 可能还遇到其他的上结错误 .
使用非预定义异常的步骤如下 :
    定义异常 -> 关联异常和错误 -> 引用例外
当定义 Oracle 错误和例外之间的关联关系时 , 要使用伪过程 EXCEPTION_INTI. 下面以处理 ORA-02291 错误为例说明 :
DECLARE
  e_integrity EXCEPTION;
  PRAGMA EXCEPTION_INIT(e_integrity, -2291);
BEGIN
  UPDATE emp SET deptno = &dno WHERE empno = &eno;
EXCEPTION
  WHEN e_integrity THEN
    DBMS_OUTPUT.PUT_LINE(' 该部门不存在 .');
END;
 
# 处理自定义异常
预定义异常和非预定义异常都与 Oracle 错误有关 , 并且出现 Oracle 错误时会自动触发 .
而自定义异常与 Oracle 错误没有任何关联 , 它是开发人员为特定情况所定义的异常 .
自定义异常必须显式触发 . 使用步骤如下 :
    定义异常 -> 显式触发异常 -> 引用异常
首先要在定义部分 (DECLARE) 定义异常 , 然后在执行部分 (BEGIN) 触发异常 (RAISE 语句 ),
最后在异常处理部分 (EXCEPTION) 捕捉处理 . 如下 :
DECLARE
  e_integrity EXCEPTION;
  PRAGMA EXCEPTION_INIT(e_integrity, -2291);
  e_no_employee EXCEPTION;
BEGIN
  UPDATE emp SET deptno = &dno WHERE empno = &eno;
  IF SQL%NOTFOUND THEN
    RAISE e_no_employee;
  END IF;
EXCEPTION
  WHEN e_integrity THEN
    DBMS_OUTPUT.PUT_LINE(' 该部门不存在 .');
  WHEN e_no_employee THEN
    DBMS_OUTPUT.PUT_LINE(' 该雇员不存在 .');
END;
/
# 使用异常函数
PL/SQL 块中出现 Oracle 错误时 , 通过使用例外函数可以取得错误号以及相关的错误消息 , 函数 SQLCODE 用于取得 Oracle 错误号 .
SQLERRM 则用于取得与之相关的错误消息 . 另外 , 在存储过程 , 函数和包中使用 RAISE_APPLICATION_ERROR 可以自定义错误号和消息 .
 
DECLARE
  v_ename emp.ename%TYPE;
BEGIN
  SELECT ename INTO v_ename FROM emp WHERE sal = &&v_sal;
  DBMS_OUTPUT.PUT_LINE(' 雇员名 :' || v_ename);
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE(' 不存在工资为 ' || &v_sal || ' 雇员 ');
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(' 错误号 ' || SQLCODE);
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/
# RAISE_APPLICATION_ERROR
该过程用于自定义错误消息 . 只能在数据库端的子程序 ( 过程 , 函数 , , 触发器 ) 中使用 , 不能在匿名块和客户端程序是使用 .
语法如下 :
    raise_application_error(error_number,message[,{TRUE | FALSE}]);
error_number : 错误号 , 范围是 : -20000 ~ -20999 之间的负整数 ;
message :      错误消息 , 长度不能超过 2048 字节 ;
第三个可靠选参数 , 如果 TRUE, 该错误会被放在先前错误堆栈中 ; 如果 FALSE( 默认 ), 则替换先前所有错误 .
 
:
CREATE OR REPLACE PROCEDURE raise_comm(eno NUMBER, commission NUMBER) IS
  v_comm emp.comm%TYPE;
BEGIN
  SELECT comm INTO v_comm FROM emp WHERE emp = eno;
  IF v_comm IS NULL THEN
    RAISE_APPLICATION_ERROR(-20001, ' 该员工无补助 .');
  END IF;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE(' 该雇员不存在 .');
END;
/
## PL/SQL 编译警告
Oracle 10g 之前 , 编写 PL/SQL 子程序时 , 只要子程序符合 SQL PL/SQL 的语法及主义规则 ,Oracle 就会成功地编译子程序 .
如下 (DEAD CODE):
CREATE OR REPLACE PROCEDURE dead_code AS
  x number := 10;
BEGIN
  IF x = 10 THEN
    x = 20;
  ELSE
    x := 100; -- 死代码
  END IF;
END dead_code;
/
如例 ,ELSE 子句永远不会执行 , 应该避免出现类似的死代码 .Oracle 10g 开始 ,
在编写 PL/SQL 子程序之前开发人员可以激活警告检查 .
 
1.PL/SQL 警告分类
SEVERE: 用于检查可能出现的不可预料结果或错误结果 , 例如参数的别名问题 .
PERFORMANCE: 用于检查可能引起性能问题 , 如在 INSERT 操作是为 NUMBER 列提供了 VARCHAR2 类型数据 .
INFORMATIONAL: 用于检查程序中的死代码 .
ALL: 用于检查所有警告 .
 
2. 控制 PL/SQL 警告消息
为了使得数据库可以在编译 PL/SQL 子程序时发出警告消息 , 需要设置寝化参数 PLSQL_WARNINGS.
不仅可以在系统级或会话级设置 , 也可以在 ALTER PROCEDURE 命令是进行设置 .
既可以激活或禁止所有警告类型 , 也可以激活或禁止特定消息号 . :
SQL> ALTER SYSTEM SET PLSQL_WARNINGS='ENABLE:ALL';
SQL> ALTER SESSION SET PLSQL_WARNINGS='ENABLE:PERFORMANCE';
SQL> ALTER PROCEDURE hello COMPILE;
2> PLSQL_WARNINGS='ENABLE:PERFORMANCE';
SQL> ALTER SESSION SET PLSQL_WARNINGS='DISABLE:ALL';
SQL> ALTER SESSION SET PLSQL_WARNINGS='ENABLE:SEVERE',
2> 'DISABLE:PERFORMANCE','ERROR:0 6002 ';
 
激活警告检查后 , 编译子程序时用 SHOW ERRORS 命令显示警告错误 .

你可能感兴趣的:(oracle,exception,table,application,performance,Warnings)