Oracle PL/SQL 中的异常处理(示例)
什么是 PL/SQL 中的异常处理?
当 PL/SQL 引擎遇到因运行时错误而无法执行的指令时,就会发生异常。这些错误在编译时不会被捕获,因此只能在运行时处理。
例如,如果 PL/SQL 引擎收到将任何数字除以“0”的指令,则 PL/SQL 引擎会将其作为异常抛出。异常仅在运行时由 PL/SQL 引擎引发。
异常会阻止程序进一步执行,因此要避免这种情况,需要单独捕获和处理它们。这个过程称为异常处理,在此过程中,程序员会处理运行时可能发生的异常。
异常处理语法
异常是在块级别处理的,也就是说,如果任何块中发生任何异常,控制权就会退出该块的执行部分。然后,异常将在该块的异常处理部分进行处理。处理异常后,无法将控制权重新发送回该块的执行部分。
下面的语法解释了如何捕获和处理异常。
BEGIN <execution block> . . EXCEPTION WHEN <exceptionl_name> THEN <Exception handling code for the “exception 1 _name’' > WHEN OTHERS THEN <Default exception handling code for all exceptions > END;
语法说明
- 在上面的语法中,异常处理块包含一系列 WHEN 条件来处理异常。
- 每个 WHEN 条件后面都跟着预计在运行时引发的异常名称。
- 当运行时引发任何异常时,PL/SQL 引擎会在异常处理部分查找该特定异常。它将从第一个“WHEN”子句开始,按顺序搜索。
- 如果找到已引发异常的处理方法,它将执行该特定处理代码部分。
- 如果不存在已引发异常的“WHEN”子句,则 PL/SQL 引擎将执行(如果存在)“WHEN OTHERS”部分。这对于所有异常都是通用的。
- 执行异常后,控制权将退出当前块。
- 每个块在运行时最多只能执行一个异常部分。执行完毕后,控制器将跳过其余的异常处理部分并退出当前块。
注意: WHEN OTHERS 应始终位于序列的最后一个位置。WHEN OTHERS 之后的异常处理部分将永远不会被执行,因为在执行 WHEN OTHERS 后控制权将退出块。
异常类型
PL/SQL 中有两种类型的异常。
- 预定义异常
- 用户定义异常
预定义异常
Oracle 预定义了一些常见的异常。这些异常具有唯一的异常名称和错误编号。这些异常已在 Oracle 的“STANDARD”包中定义。在代码中,我们可以直接使用这些预定义的异常名称来处理它们。
以下是一些预定义的异常
Exception | 错误代码 | 异常原因 |
---|---|---|
ACCESS_INTO_NULL | ORA-06530 | 为未初始化的对象的属性赋值 |
CASE_NOT_FOUND | ORA-06592 | CASE 语句中的所有“WHEN”子句均不满足且未指定“ELSE”子句 |
COLLECTION_IS_NULL | ORA-06531 | 在未初始化的集合上使用集合方法(EXISTS 除外)或访问集合属性 |
CURSOR_ALREADY_OPEN | ORA-06511 | 尝试打开一个已打开的游标 |
DUP_VAL_ON_INDEX | ORA-00001 | 将重复值存储到受唯一索引约束的数据库列中 |
INVALID_CURSOR | ORA-01001 | 非法的游标操作,例如关闭一个未打开的游标 |
INVALID_NUMBER | ORA-01722 | 由于无效的数字字符,字符转换为数字失败 |
NO_DATA_FOUND | ORA-01403 | 当包含 INTO 子句的“SELECT”语句没有获取到任何行时。 |
ROW_MISMATCH | ORA-06504 | 当游标变量数据类型与实际游标返回类型不兼容时 |
SUBSCRIPT_BEYOND_COUNT | ORA-06533 | 通过大于集合大小的索引号引用集合 |
SUBSCRIPT_OUTSIDE_LIMIT | ORA-06532 | 通过超出合法范围的索引号(例如:-1)引用集合 |
TOO_MANY_ROWS | ORA-01422 | 当包含 INTO 子句的“SELECT”语句返回多于一行时 |
VALUE_ERROR | ORA-06502 | 算术或大小约束错误(例如:将一个值赋给一个比变量大小大的变量) |
ZERO_DIVIDE | ORA-01476 | 将一个数字除以“0” |
用户定义异常
在 Oracle 中,除了上述预定义异常之外,程序员还可以创建自己的异常并进行处理。它们可以在子程序的声明部分进行创建。这些异常仅在该子程序中可见。在包规范中定义的异常是公共异常, wherever the package is accessible. wherever the package is accessible.
语法:在子程序级别
DECLARE <exception_name> EXCEPTION; BEGIN <Execution block> EXCEPTION WHEN <exception_name> THEN <Handler> END;
- 在上面的语法中,“exception_name”变量被定义为“EXCEPTION”类型。
- 这可以像预定义异常一样使用。
语法:在包规范级别
CREATE PACKAGE <package_name> IS <exception_name> EXCEPTION; . . END <package_name>;
- 在上面的语法中,“exception_name”变量在<package_name>的包规范中被定义为“EXCEPTION”类型。
- 这可以在数据库中 wherever package ‘package_name’ can be called.
PL/SQL 引发异常
所有预定义异常都会在发生错误时隐式引发。但用户定义的异常需要显式引发。这可以通过关键字“RAISE”来实现。它可以通过以下任何一种方式使用。
如果在程序中单独使用“RAISE”,它会将已引发的异常传播到父块。只能在异常块中使用,如下所示。
CREATE [ PROCEDURE | FUNCTION ] AS BEGIN <Execution block> EXCEPTION WHEN <exception_name> THEN <Handler> RAISE; END;
语法说明
- 在上面的语法中,关键字 RAISE 在异常处理块中使用。
- 每当程序遇到“exception_name”异常时,该异常都会被处理并正常完成。
- 但是,异常处理部分中的“RAISE”关键字会将此特定异常传播到父程序。
注意:在将异常引发到父块时,正在引发的异常也应该在父块中可见,否则 Oracle 将抛出错误。
- 我们可以使用关键字“RAISE”后跟异常名称来引发该特定用户定义/预定义异常。这可以在执行部分和异常处理部分中使用来引发异常。
CREATE [ PROCEDURE | FUNCTION ] AS BEGIN <Execution block> RAISE <exception_name> EXCEPTION WHEN <exception_name> THEN <Handler> END;
语法说明
- 在上面的语法中,关键字 RAISE 在执行部分中使用,后跟异常“exception_name”。
- 这将在执行期间引发此特定异常,并且需要进行处理或进一步引发。
示例 1:在此示例中,我们将看到
- 如何声明异常
- 如何引发已声明的异常,以及
- 如何将其传播到主块
DECLARE Sample_exception EXCEPTION; PROCEDURE nested_block IS BEGIN Dbms_output.put_line(‘Inside nested block’); Dbms_output.put_line(‘Raising sample_exception from nested block’); RAISE sample_exception; EXCEPTION WHEN sample_exception THEN Dbms_output.put_line (‘Exception captured in nested block. Raising to main block’); RAISE, END; BEGIN Dbms_output.put_line(‘Inside main block’); Dbms_output.put_line(‘Calling nested block’); Nested_block; EXCEPTION WHEN sample_exception THEN Dbms_output.put_line (‘Exception captured in main block'); END: /
代码解释
- 代码行 2:将变量“sample_exception”声明为 EXCEPTION 类型。
- 代码行 3:声明过程 nested_block。
- 代码行 6:打印语句“Inside nested block”。
- 代码行 7:打印语句“Raising sample_exception from nested block。”
- 代码行 8:使用“RAISE sample_exception”引发异常。
- 代码行 10:嵌套块中异常 sample_exception 的异常处理程序。
- 代码行 11:打印语句“Exception captured in nested block. Raising to main block”。
- 代码行 12:将异常引发到主块(传播到主块)。
- 代码行 15:打印语句“Inside the main block”。
- 代码行 16:打印语句“Calling nested block”。
- 代码行 17:调用 nested_block 过程。
- 代码行 18:异常
- 代码行 19:主块中 sample_exception 的异常处理程序。
- 代码行 20:打印语句“Exception captured in the main block。”
异常重要注意事项
- 在函数中,异常总是应该要么返回值,要么进一步引发异常。否则,Oracle 将在运行时抛出“Function returned without a value”错误。
- 事务控制语句可以放在异常处理块中。
- SQLERRM 和 SQLCODE 是内置函数,可提供异常消息和代码。
- 如果未处理异常,则默认情况下该会话中的所有活动事务都将被回滚。
- 可以使用 RAISE_APPLICATION_ERROR (-<error_code>, <error_message>) 代替 RAISE 来引发带有用户代码和消息的错误。错误代码应大于 20000,并以“-”作为前缀。
摘要
在本章之后,您应该能够处理 PL/SQL 异常的以下方面
- 处理异常
- 定义异常
- 引发异常
- 异常传播