Oracle PL/SQL 中的异常处理(示例)

什么是 PL/SQL 中的异常处理?

当 PL/SQL 引擎遇到因运行时错误而无法执行的指令时,就会发生异常。这些错误在编译时不会被捕获,因此只能在运行时处理。

例如,如果 PL/SQL 引擎收到将任何数字除以“0”的指令,则 PL/SQL 引擎会将其作为异常抛出。异常仅在运行时由 PL/SQL 引擎引发。

异常会阻止程序进一步执行,因此要避免这种情况,需要单独捕获和处理它们。这个过程称为异常处理,在此过程中,程序员会处理运行时可能发生的异常。

异常处理语法

异常是在块级别处理的,也就是说,如果任何块中发生任何异常,控制权就会退出该块的执行部分。然后,异常将在该块的异常处理部分进行处理。处理异常后,无法将控制权重新发送回该块的执行部分。

下面的语法解释了如何捕获和处理异常。

Exception Handling in 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 中有两种类型的异常。

  1. 预定义异常
  2. 用户定义异常

预定义异常

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”,它会将已引发的异常传播到父块。只能在异常块中使用,如下所示。

PL/SQL Raise Exception

CREATE [ PROCEDURE | FUNCTION ]
 AS
BEGIN
<Execution block>
EXCEPTION
WHEN <exception_name> THEN 
             <Handler>
RAISE;
END;

语法说明

  • 在上面的语法中,关键字 RAISE 在异常处理块中使用。
  • 每当程序遇到“exception_name”异常时,该异常都会被处理并正常完成。
  • 但是,异常处理部分中的“RAISE”关键字会将此特定异常传播到父程序。

注意:在将异常引发到父块时,正在引发的异常也应该在父块中可见,否则 Oracle 将抛出错误。

  • 我们可以使用关键字“RAISE”后跟异常名称来引发该特定用户定义/预定义异常。这可以在执行部分和异常处理部分中使用来引发异常。

PL/SQL Raise Exception

CREATE [ PROCEDURE | FUNCTION ] 
AS
BEGIN
<Execution block>
RAISE <exception_name>
EXCEPTION
WHEN <exception_name> THEN
<Handler>
END;

语法说明

  • 在上面的语法中,关键字 RAISE 在执行部分中使用,后跟异常“exception_name”。
  • 这将在执行期间引发此特定异常,并且需要进行处理或进一步引发。

示例 1:在此示例中,我们将看到

  • 如何声明异常
  • 如何引发已声明的异常,以及
  • 如何将其传播到主块

PL/SQL Raise Exception

PL/SQL Raise Exception

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 异常的以下方面

  • 处理异常
  • 定义异常
  • 引发异常
  • 异常传播