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 预定义异常种类
Exception | Oracle Error | SQLCODE Value |
---|---|---|
ACCESS_INTO_NULL |
ORA-06530 |
-6530 |
CASE_NOT_FOUND |
ORA-06592 |
-6592 |
COLLECTION_IS_NULL |
ORA-06531 |
-6531 |
CURSOR_ALREADY_OPEN |
ORA-06511 |
-6511 |
DUP_VAL_ON_INDEX |
ORA-00001 |
-1 |
INVALID_CURSOR |
ORA-01001 |
-1001 |
INVALID_NUMBER |
ORA-01722 |
-1722 |
LOGIN_DENIED |
ORA-01017 |
-1017 |
NO_DATA_FOUND |
ORA-01403 |
+100 |
NOT_LOGGED_ON |
ORA-01012 |
-1012 |
PROGRAM_ERROR |
ORA-06501 |
-6501 |
ROWTYPE_MISMATCH |
ORA-06504 |
-6504 |
SELF_IS_NULL |
ORA-30625 |
-30625 |
STORAGE_ERROR |
ORA-06500 |
-6500 |
SUBSCRIPT_BEYOND_COUNT |
ORA-06533 |
-6533 |
SUBSCRIPT_OUTSIDE_LIMIT |
ORA-06532 |
-6532 |
SYS_INVALID_ROWID |
ORA-01410 |
-1410 |
TIMEOUT_ON_RESOURCE |
ORA-00051 |
-51 |
TOO_MANY_ROWS |
ORA-01422 |
-1422 |
VALUE_ERROR |
ORA-06502 |
-6502 |
ZERO_DIVIDE |
ORA-01476 |
-1476 |
说明一下:“| :--: | :--: | :--: |”中的--左边:表示左对齐,右边:表示右对齐,两边都有表示居中对齐。
1.2 预定义异常发生场景
ID | Exception | Raised when ... |
---|---|---|
1 | ACCESS_INTO_NULL | Your program attempts to assign values to the attributes of an uninitialized (atomically null) object. |
2 | CASE_NOT_FOUND | None of the choices in the WHEN clauses of a CASE statement is selected, and there is no ELSE clause. |
3 | COLLECTION_IS_NULL | Your 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. |
4 | CURSOR_ALREADY_OPEN | Your 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. |
5 | DUP_VAL_ON_INDEX | Your program attempts to store duplicate values in a database column that is constrained by a unique index. |
6 | INVALID_CURSOR | Your program attempts an illegal cursor operation such as closing an unopened cursor. |
7 | INVALID_NUMBER | In 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. |
8 | LOGIN_DENIED | Your program attempts to log on to Oracle with an invalid username and/or password. |
9 | NO_DATA_FOUND | A 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. |
10 | NOT_LOGGED_ON | Your program issues a database call without being connected to Oracle. |
11 | PROGRAM_ERROR | PL/SQL has an internal problem. |
12 | ROWTYPE_MISMATCH | The 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. |
13 | SELF_IS_NULL | Your 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. |
14 | STORAGE_ERROR | PL/SQL runs out of memory or memory has been corrupted. |
15 | SUBSCRIPT_BEYOND_COUNT | Your program references a nested table or varray element using an index number larger than the number of elements in the collection. |
16 | SUBSCRIPT_OUTSIDE_LIMIT | Your program references a nested table or varray element using an index number (-1 for example) that is outside the legal range. |
17 | SYS_INVALID_ROWID | The conversion of a character string into a universal rowid fails because the character string does not represent a valid rowid. |
18 | TIMEOUT_ON_RESOURCE | A time-out occurs while Oracle is waiting for a resource. |
19 | TOO_MANY_ROWS | A SELECT INTO statement returns more than one row. |
20 | VALUE_ERROR | An 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.) |
21 | ZERO_DIVIDE | Your 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抛出异常的三种方法
-
- Raise exception:用于抛出当前程序中定义的异常或在 standard 中的系统异常。
-
- Raise package.exception:用于抛出有一些异常是定义在非标准包中的,如UTL_FILE,DBMS_SQL以及程序员创建的包中异常
-
- 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;
不同处理规则如图所示: