PL/SQL 异常处理

2025 年 2 月 14 日 | 11 分钟阅读

引言

在本文中,我们将讨论 PL/SQL 异常、其类型以及如何通过各种示例进行处理。没有人是完美的,每个人都会以一种或另一种形式犯一些错误。编程错误通常在尝试执行程序之前不会被发现。由于错误会阻止程序成功执行,因此应在执行前将其删除。为了处理这些错误,PL/SQL 提供了异常。

什么是异常

在 PL/SQL 中,程序执行期间发生的错误称为异常。

PL/SQL 使程序员能够使用程序中的异常块捕获此类情况,并针对错误情况采取适当的措施。

PL/SQL Exception Handling

上图显示,当引发执行并将控制权转移到程序的异常部分时,它无法返回到程序的执行,并且 PL/SQL 块成功终止。

PL/SQL Exception Handling

如果上图显示在块的可执行部分中引发了异常,并且没有相应的异常处理程序,则该异常将转发到宿主环境 SQL * PLUS,此时环境确定如何响应异常。

异常有两种类型

  • 系统定义的异常/预定义的异常
  • 用户定义的异常

PL/SQL 异常处理

异常处理程序是程序的一部分,您可以在其中编写代码来处理代码在程序执行期间可能遇到的各种问题。每当在可执行部分中遇到错误时,它将退出可执行部分并在异常部分中搜索与错误匹配的异常处理程序。如果找到,它将执行该异常处理的代码。

它是 PL/SQL 块的最后一部分,也称为错误处理部分。它以关键字 <EXCEPTION> 开头。每个异常部分可以进一步细分为单个异常处理程序。

异常处理的语法

以下是异常处理的通用语法

在上面的语法中,<DECLARE> 部分用于声明,例如变量、函数等,<BEGIN> 部分用于程序执行的开始,<EXCEPTION> 部分用于处理部分,其中 exception1、exception2、exception3 可能是预定义的异常或由布尔逻辑运算符(例如 OR 和 AND)分隔的 oracle 预定义异常的组合。它也可以是 PRAGMA EXCEPTION_INIT 异常。它还提供了一个可选的异常处理子句,称为 "others",用于捕获所有未指定的异常。最后一部分是 <END> 部分,用于结束 PL/SQL 程序。

异常处理示例

让我们举一个简单的例子来演示异常处理的概念。这里我们使用已经创建的 CUSTOMERS 表。


ID姓名年龄地址工资
1Ramesh23Allahabad20000
2Suresh22Kanpur22000
3Mahesh24Ghaziabad24000
4Chandan25Noida26000
5Alex21巴黎28000
6Sunita20德里30000

在 SQL 提示符下执行上述代码后,会产生以下结果

No such customer!
PL/SQL procedure successfully completed.

PL/SQL 用户定义的异常

PL/SQL 方便其用户根据程序的需要定义自己的异常。可以使用 RAISE 语句或过程 DBMS_STANDARD.RAISE_APPLICATION_ERROR 显式引发用户定义的异常。例如: 在员工管理系统的情况下,员工代码的数据库必须以字母 "E" 开头。因此,应用程序开发人员会应用检查以确保不会发生这种情况。每当程序中发生错误时,就会引发这些用户定义的异常。

声明用户定义的异常

异常的声明与变量类似,仅在 PL/SQL 块的声明部分中声明。它被分配一个唯一的名称,用于在程序中引用该异常。

用户定义异常的语法

在上面的语法中,my_exception 是异常的名称。

引发用户定义的异常

在任何内部数据库错误的情况下,异常由数据库服务器自动引发。但程序员也可以使用命令 RAISE 显式引发异常。

引发异常的语法

在上面的语法中,每当使用 raised 语句调用定义的异常时,程序控制权就会传递给定义的用于处理该异常的异常处理程序。

示例

编写一个 PL/SQL 程序,该程序选择给定部门编号的部门名称和位置,如果该部门编号不存在,则该程序会引发用户定义的异常。

说明

执行时,它会提示您输入要获取其信息的 deptno。如果您输入现有的部门编号,它将获取该部门编号的部门名称和位置。如果您输入无效的部门编号,它将引发异常 "deptno_err" 并提示您输入相应的消息。

用户定义异常的作用域规则

  • 用户定义的异常不能在同一个块中声明两次。
  • 在外部块中声明的异常在嵌套块中可见,但外部块无法引用子块中声明的异常。

PL/SQL 预定义的异常

PL/SQL 提供了一组预定义的异常,这些异常由系统在运行时内部隐式引发。如果在 PL/SQL 中遇到错误,则会发生这种情况。每当 Oracle 在运行时遇到系统错误时,都会隐式引发相应的预定义异常,因此正常执行停止,控制权转移到程序中指定的异常处理程序。PL/SQL 中有许多预定义的异常,当程序违反任何数据库规则时,这些异常会执行。例如: NO_DATA_FOUND 是一个预定义的异常,当 SELECT INTO 语句未返回任何行时,会引发该异常。

以下是一些重要的预定义异常的列表

ExceptionOracle 错误SQL 代码描述
ACCESS_INTO_NULL06530-6530当 NULL 对象自动分配一个值时,会引发此异常。
CASE_NOT_FOUND06592-6592当 CASE 语句的 "WHEN" 子句中没有选择任何选项,并且没有 else 子句时,会引发此异常。
COLLECTION_IS_NULL06531-6531当程序尝试将集合方法(而不是 exists)应用于未初始化的嵌套表或 varray,或者程序尝试将值分配给未初始化的嵌套表或 varray 的元素时,会引发此异常。
DUP_VAL_ON_INDEX00001-1当尝试将重复值存储到具有唯一索引的列中时,会引发此异常。
INVALID_CURSOR01001-1001当尝试执行不允许的游标操作时,例如关闭未打开的游标,会引发此异常。
INVALID_NUMBER01722-1722当将字符串转换为数字失败时,因为该字符串不代表有效的数字,会引发此异常。
LOGIN_DENIED01017-1017当程序尝试使用无效的用户名或密码登录数据库时,会引发此异常。
NO_DATA_FOUND01403+100当 select into 语句未返回任何行时,会引发此异常。
NOT_LOGGED_ON01012-1012当在未连接到数据库的情况下发出数据库调用时,会引发此异常。
PROGRAM_ERROR06501-6501当 PL/SQL 遇到内部问题时,会引发此异常。
ROWTYPE_MISMATCH06504-6504当游标在具有不兼容数据类型的变量中获取值时,会引发此异常。
SELF_IS_NULL30625-30625当调用成员方法时,但未初始化对象类型的实例时,会引发此异常。
STORAGE_ERROR06500-6500当 PL/SQL 内存不足或内存损坏时,会引发此异常。
TOO_MANY_ROWS01422-1422当 SELECT INTO 语句返回多于一行时,会引发此异常。
VALUE_ERROR06502-6502当发生算术、转换、截断或大小约束错误时,会引发此异常。
ZERO_DIVIDE014761476当尝试将数字除以零时,会引发此异常。

示例

编写一个 PL/SQL 程序,该程序可以处理多个异常。

说明

在上面的编程示例中,在执行时,由于 SELECT INTO 语句将返回多于一行,这是不可能的,因此会引发异常。通过这种方式,控制权转移到异常部分,并调用 TOO_MANY_ROWS 异常处理程序,然后显示 DBMS_OUTPUT 程序包中包含的消息,即 "返回多于一行"。

使用 PRAGMA EXCEPTION_INIT

使用 PRAGMA _ EXCEPTION_INIT 来处理未命名的内部异常。PL/SQL 开发人员可以使用它将用户定义的异常与内部错误代码关联起来。在本例中

  • Pragma 用于将名称与内部错误代码一起包含。
  • EXCEPTION_INIT 是编译时命令

建立关联后,您可以通过名称引发该异常,并编写捕获该错误的异常处理程序。它必须出现在 PL/SQL 块子程序的声明部分中。异常名称必须已在同一块、外部块或程序包规范中定义。使用以下语法:

PRAGMA EXCEPTION_INIT(exception_name, Oracle_err_num);

在上面的语法中,exception_name 是先前声明的异常的名称,Oracle_err_num 是 Oracle 错误的编号。它可以是具有以下约束的整数值:

  • 它不能为零或 1 之外的任何正数。
  • 它不能为 -1403,这是 NO_DATA_FOUND 的 Oracle_err_num,但如果您想为其指定一个数字,则将 100 传递给 EXCEPTION_INT Pragma。

示例

编写一个程序,如果用户想要将用户定义的异常名称与 ORACLE_ERR_NUM 2292 关联,则只能通过使用 PRAGMA EXCEPTION_INIT。

说明

执行时,它会显示消息 "首先删除子记录"。执行 Delete 语句时,会生成一个 Oracle 错误,其 err_num 为 -2292。现在,在声明部分中,我们声明了一个 PRAGMA 异常。因此,在遇到错误时,控制权将转移到 EXCEPTION 部分,并在其中打印相应的消息。

使用 RAISE_APPLICATION_Error

Oracle 提供了一种 RAISE_APPLICATION_Error 方法,用于在您的应用程序中引发特定于应用程序的错误。它在 DBMS_STANDARD 程序包中定义。使用 RAISE_APPLICATION_ERROR 而不是 "raise" 的主要目的是您可以将错误消息与异常关联起来。

使用以下语法来调用 RAISE_APPLICATION_ERROR

在上面的语法中,error_number 是 -20999 到 -20000 范围内的负整数。第二个参数是错误消息,长度不应超过 2KB 个字符。如果最后一个参数,则将该错误放置在先前错误的堆栈中,否则,它将替换所有当前错误。默认情况下,它是 FALSE。

示例:使用 RAISE_APPLICATION_Error 的程序

说明

执行时,它将提示用户输入员工代码。如果我们输入佣金不存在的员工的 empcode(即 7342),则它将显示错误编号以及错误消息。但是,将为佣金存在的员工(即 7899)添加 empcode,然后将佣金更新 50%。

在处理程序子句中使用 SQLCODE 和 SQLERRM

以下是两个可以在任何异常处理程序中使用的内置函数,当与 WHEN OTHERS 子句结合使用时,它们变得非常有用。由于抛出的任何异常都将在 WHEN OTHERS 异常处理程序中捕获,因此使用像 SQLCODE 和 SQLERRM 这样的错误捕获函数可以提供一种自描述的错误机制。

SQLCODE: 它是用于标识先前执行的 PL/SQL 语句的错误的错误代码。

SQLCODE 返回

  • 0:SQL 语句执行没有错误。
  • +1:找到用户定义的异常。
  • +100:发生 Oracle 错误 NO_DATA_FOUND。
  • 负数:对于其他的 Oracle 内部异常。

SQLERRM: 它将显示您传递给它的错误号的错误消息。它包含一个错误消息,即 512 个字符长,错误代码和嵌套消息,例如表名和列名。

示例:在处理程序子句中使用 SQLCODE 和 SQLERRM 的程序

说明

执行时,它将提示您输入 Empno。如果 Empno 存在于表中,它将显示相应的员工工资。否则,将引发异常,并显示错误代码和消息。

EXCEPTION PROPAGATION 的使用

引发异常时,PL/SQL 会在异常的当前块中查找异常处理程序。如果在异常部分中找到异常处理程序,则子块处理异常并正常终止。控制权在子块 END 语句之后的 CLOSE 块中立即恢复。如果找不到处理程序,PL/SQL 会将异常传播到该当前块的封闭块。然后,PL/SQL 尝试通过在封闭块中再次引发异常来处理该异常。它将继续,直到合适的异常处理程序捕获了该异常,或者可执行代码停止处理并将错误返回给运行 PL/SQL 的宿主环境。

示例:使用 EXCEPTION PROPAGATION 的程序

说明

在上面的例子中,它会要求你输入员工编号 (E),然后检查工资是否大于 5000。如果为真,它会引发异常,并在当前块中搜索异常处理程序。如果在处理程序块中没有找到,则尝试将异常与异常部分事件处理程序匹配。当这个异常处理程序找到 "greater_year" 并执行相应的语句。

PL/SQL 异常的好处

以下是 PL/SQL 异常的各种好处。

  • 通过使用异常处理,可以轻松处理错误,而无需编写多个检查。
  • 它通过隔离错误处理例程来提高可读性。如果 PL/SQL 块具有异常处理程序,并且如果在该块中引发了异常,则异常处理程序将处理该异常。

下一个主题PL/SQL 触发器