Oracle Exception

Oracle异常的文档在这里:https://docs.oracle.com/cd/B10501_01/appdev.920/a96624/07_errs.htm

总的来讲,Oracle Exception分为如下3类:

  • 预定义异常
  • 非预定义异常-
  • 自定义异常

处理方法也分为3类:
处理方法分为:直接抛出异常、内部块处理异常、游标处理异常

  • 预定义异常:由PL/SQL定义的异常。由于它们已在standard包中预定义了,因此,这些预定义异常可以直接在程序中使用,而不必再定义部分声明。
  • 非预定义异常:用于处理预定义异常所不能处理的Oracle错误。
  • 自定义异常:用户自定义的异常,需要在定义部分声明后才能在可执行部分使用。用户自定义异常对应的错误不一定是Oracle错误,例如它可能是一个数据错误。

三种异常中,预定义与非预定义异常都与Oracle错误有关,并且由Oracle隐含自动抛出,而自定义异常与Oracle错误没有任何关联,由开发人员为特定情况所定义的异常,需要显式抛出(raise)。

1.预定义异常

1.1 预定义异常种类

ExceptionOracle ErrorSQLCODE Value
ACCESS_INTO_NULLORA-06530-6530
CASE_NOT_FOUNDORA-06592-6592
COLLECTION_IS_NULLORA-06531-6531
CURSOR_ALREADY_OPENORA-06511-6511
DUP_VAL_ON_INDEXORA-00001-1
INVALID_CURSORORA-01001-1001
INVALID_NUMBERORA-01722-1722
LOGIN_DENIEDORA-01017-1017
NO_DATA_FOUNDORA-01403+100
NOT_LOGGED_ONORA-01012-1012
PROGRAM_ERRORORA-06501-6501
ROWTYPE_MISMATCHORA-06504-6504
SELF_IS_NULLORA-30625-30625
STORAGE_ERRORORA-06500-6500
SUBSCRIPT_BEYOND_COUNTORA-06533-6533
SUBSCRIPT_OUTSIDE_LIMITORA-06532-6532
SYS_INVALID_ROWIDORA-01410-1410
TIMEOUT_ON_RESOURCEORA-00051-51
TOO_MANY_ROWSORA-01422-1422
VALUE_ERRORORA-06502-6502
ZERO_DIVIDEORA-01476-1476
说明一下:“| :--: | :--: | :--: |”中的--左边:表示左对齐,右边:表示右对齐,两边都有表示居中对齐。

1.2 预定义异常发生场景

IDExceptionRaised when …
1ACCESS_INTO_NULLYour program attempts to assign values to the attributes of an uninitialized (atomically null) object.
2CASE_NOT_FOUNDNone of the choices in the WHEN clauses of a CASE statement is selected, and there is no ELSE clause.
3COLLECTION_IS_NULLYour program attempts to apply collection methods other than EXISTS to an uninitialized (atomically null) nested table or varray, or the program attempts to assign values to the elements of an uninitialized nested table or varray.
4CURSOR_ALREADY_OPENYour program attempts to open an already open cursor. A cursor must be closed before it can be reopened. A cursor FOR loop automatically opens the cursor to which it refers. So, your program cannot open that cursor inside the loop.
5DUP_VAL_ON_INDEXYour program attempts to store duplicate values in a database column that is constrained by a unique index.
6INVALID_CURSORYour program attempts an illegal cursor operation such as closing an unopened cursor.
7INVALID_NUMBERIn a SQL statement, the conversion of a character string into a number fails because the string does not represent a valid number. (In procedural statements, VALUE_ERROR is raised.) This exception is also raised when the LIMIT-clause expression in a bulk FETCH statement does not evaluate to a positive number.
8LOGIN_DENIEDYour program attempts to log on to Oracle with an invalid username and/or password.
9NO_DATA_FOUNDA SELECT INTO statement returns no rows, or your program references a deleted element in a nested table or an uninitialized element in an index-by table. SQL aggregate functions such as AVG and SUM always return a value or a null. So, a SELECT INTO statement that calls an aggregate function never raises NO_DATA_FOUND. The FETCH statement is expected to return no rows eventually, so when that happens, no exception is raised.
10NOT_LOGGED_ONYour program issues a database call without being connected to Oracle.
11PROGRAM_ERRORPL/SQL has an internal problem.
12ROWTYPE_MISMATCHThe host cursor variable and PL/SQL cursor variable involved in an assignment have incompatible return types. For example, when an open host cursor variable is passed to a stored subprogram, the return types of the actual and formal parameters must be compatible.
13SELF_IS_NULLYour program attempts to call a MEMBER method on a null instance. That is, the built-in parameter SELF (which is always the first parameter passed to a MEMBER method) is null.
14STORAGE_ERRORPL/SQL runs out of memory or memory has been corrupted.
15SUBSCRIPT_BEYOND_COUNTYour program references a nested table or varray element using an index number larger than the number of elements in the collection.
16SUBSCRIPT_OUTSIDE_LIMITYour program references a nested table or varray element using an index number (-1 for example) that is outside the legal range.
17SYS_INVALID_ROWIDThe conversion of a character string into a universal rowid fails because the character string does not represent a valid rowid.
18TIMEOUT_ON_RESOURCEA time-out occurs while Oracle is waiting for a resource.
19TOO_MANY_ROWSA SELECT INTO statement returns more than one row.
20VALUE_ERRORAn arithmetic, conversion, truncation, or size-constraint error occurs. For example, when your program selects a column value into a character variable, if the value is longer than the declared length of the variable, PL/SQL aborts the assignment and raises VALUE_ERROR. In procedural statements, VALUE_ERROR is raised if the conversion of a character string into a number fails. (In SQL statements, INVALID_NUMBER is raised.)
21ZERO_DIVIDEYour program attempts to divide a number by zero.

中文解释参考:https://blog.csdn.net/bbliutao/article/details/7833721

2.非预定义异常

非预定义异常用于处理与上述21个预定义异常无关的Oracle错误,如果要处理没有与预定义异常对应的Oracle错误时,则需要为这些Oracle错误声明相应的非预定义异常。
声明这样的异常需要使用exception_init编译指令。exception_init编译指令的定义如下:

pragma exception_init(exception_name,Oracle_error_number);
说明:exception_name是预先被声明的异常名,Oracle_error_number是错误号,这条命令必须写在定义部分。

例子

declare
    e_inte exception; --定义
    pragma exception_intt(e_inte,-2291); --关联Oracle错误ORA-2291
begin
    update emp set deptno=&dno where empno=&eno;
exception
    when e_inte then
    dbms_output.put_line('部门不存在');
end;

官方例子

DECLARE
   deadlock_detected EXCEPTION;
   PRAGMA EXCEPTION_INIT(deadlock_detected, -60);
BEGIN
   ... -- Some operation that causes an ORA-00060 error
EXCEPTION
   WHEN deadlock_detected THEN
      -- handle the error
END;

3.自定义异常

3.1 语法定义

自定义异常与Oracle错误没有任何关联,由开发人员为特定情况所定义的异常,需要显式抛出(raise)。其具有特点是:

  • 自定义异常的声明与变量的声明类似,但异常是一个错误状态,而不是一个数据项。
  • 因为不是数据项,所以异常不能出现在赋值语句和sql语句中,但异常的作用域与定义部分其它变量的作用域相同。
  • 如果一个自定义异常被传递到作用域外,则不能再通过原来的名字引用它。如果确有需要,可以在包中声明异常,这个异常就可以在任何块中使用,使用时在异常前加包名前缀即可。
CREATE PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) AS
   curr_sal NUMBER;
BEGIN
   SELECT sal INTO curr_sal FROM emp WHERE empno = emp_id;
   IF curr_sal IS NULL THEN
      /* Issue user-defined error message. */
      raise_application_error(-20101, 'Salary is missing');
   ELSE
      UPDATE emp SET sal = curr_sal + amount WHERE empno = emp_id;
   END IF;
END raise_salary;
当curr_sal 为空时会抛出异常信息。

当一个异常产生时,控制权立即转交给块的异常处理部分。如果该块没有异常处理部分,则向该块的外一层块传递。一旦控制权交给了异常处理部分,则再没有办法回到块可执行部分

3.2 raise抛出异常的三种方法

    1. Raise exception:用于抛出当前程序中定义的异常或在 standard 中的系统异常。
    1. Raise package.exception:用于抛出有一些异常是定义在非标准包中的,如UTL_FILE,DBMS_SQL以及程序员创建的包中异常
    1. Raise:不带任何参数,这种情况只出现在希望将当前的异常传到外部程序时。

3.3 生产使用

  • 抛出异常语句块,判定何种情况抛出何种异常
/*如果当前账期日没有0082山东支付对账,日结算不进行,并告警*/
IF SDP_ACC_FLOW_COUNT = 0 THEN
  RAISE EXC_SDP_NO_ACC;
ELSE
/*查询最近一条0082山东支付对账流程的状态*/
SELECT FLOW.STATE
  INTO SDP_ACC_STATE
  FROM (SELECT T.STATE,
               ROW_NUMBER() OVER(PARTITION BY T.SETTLEDATE ORDER BY T.CREATETIME DESC) ROWNUMBER
          FROM AF_FLOW T
         WHERE T.PROCESSID = P_PROCESS_ID_SDP AND T.SETTLEDATE = P_DATE) FLOW
 WHERE ROWNUMBER = 1;
  /*最近一条0082山东支付对账流程状态不是Finish,日结算不进行,并告警*/
  IF SDP_ACC_STATE <> 'Finish' THEN
    RAISE EXC_SDP_UNFINISHED_ACC;
  END IF;
END IF;
  • 异常处理块(插入告警表告警记录)
/*处理异常*/
EXCEPTION
/*当前账期日没有进行全网支付对账*/
  WHEN EXC_FNP_NO_ACC THEN
    LOG_ALARM_HELPER('[失败]全网支付日结算流程无法开始,当前账期日:' || P_DATE || ' 没有进行全网支付对账',0,'cmup-settle-pro','FNP','');
    ALARM_MESSAGE := '[失败]全网支付日结算流程无法开始,当前账期日:' || P_DATE || ' 没有进行全网支付对账';
/*当前账期日的全网支付对账未全部完成*/
  WHEN EXC_FNP_UNFINISHED_ACC THEN
    LOG_ALARM_HELPER('[失败]全网支付日结算流程无法开始,当前账期日:' || P_DATE || ' 有部分业务线未完成全网支付流程,业务线为:' || FNP_UNFINISHED_ACC,0,'cmup-settle-pro','FNP','');
    ALARM_MESSAGE := '[失败]全网支付日结算流程无法开始,当前账期日:' || P_DATE || ' 有部分业务线未完成全网支付流程,业务线为:' || FNP_UNFINISHED_ACC;
/*当前账期日没有进行0082山东支付对账*/
  WHEN EXC_SDP_NO_ACC THEN
    LOG_ALARM_HELPER('[失败]全网支付日结算流程无法开始,当前账期日:' || P_DATE || ' 没有进行0082山东支付对账',0,'cmup-settle-pro','FNP','');
    ALARM_MESSAGE := '[失败]全网支付日结算流程无法开始,当前账期日:' || P_DATE || ' 没有进行0082山东支付对账';
/*当前账期日的0082山东支付对账未全部完成*/
  WHEN EXC_SDP_UNFINISHED_ACC THEN
    LOG_ALARM_HELPER('[失败]全网支付日结算流程无法开始,当前账期日:' || P_DATE || ' 未完成0082山东支付对账流程',0,'cmup-settle-pro','FNP','');
    ALARM_MESSAGE := '[失败]全网支付日结算流程无法开始,当前账期日:' || P_DATE || ' 未完成0082山东支付对账流程';
/*系统运行异常*/
  WHEN OTHERS THEN
    LOG_ALARM_HELPER('[失败]系统异常,全网支付日结算程序出错,请查看相关运行日志!',1,'cmup-settle-pro','FNP','');
    /*将系统异常具体信息抛给后台程序打印出来*/
    RAISE;

不同处理规则如图所示:

《Oracle Exception》 pls81009_propagation_rules_example1.gif
《Oracle Exception》 pls81010_propagation_rules_example2.gif

《Oracle Exception》 pls81011_propagation_rules_example3.gif

    原文作者:西瓜雪梨桔子汁
    原文地址: https://www.jianshu.com/p/de15d5e2b702
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞