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

⚡ 智能摘要

异常处理 Oracle PL/SQL 捕获阻止代码块执行的运行时错误,让引擎将控制权转移到 EXCEPTION 部分,其中预定义、用户定义和其他处理程序可以安全地响应、引发或传播错误。

  • ⚙️ 运行时错误: 当 PL/SQL 引擎遇到无法执行的指令时,例如将一个数除以零,就会出现异常。
  • 🧱 块结构: 异常情况在 EXCEPTION 部分使用 WHEN 子句进行处理,其中 WHEN OTHERS 始终放在最后。
  • 📚 预定义例外情况: Oracle STANDARD 包中包含 NO_DATA_FOUND 和 ZERO_DIVIDE 等常见错误名称,以便直接捕获。
  • 🛠️ 用户自定义例外: 程序员可以声明自己的异常变量,并使用 RAISE 关键字显式地引发异常。
  • ⬆️ 传播: 未处理的异常会传递给封闭块,并且 RAISE 会将其重新通知父程序。
  • 🤖 人工智能辅助: AI 编码工具会在审查过程中生成 EXCEPTION 代码块并标记未处理的错误路径。

异常处理 Oracle PL / SQL

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

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

例如,如果PL/SQL引擎收到将一个数除以零的指令,它会抛出一个异常来引发错误。该异常仅在PL/SQL引擎运行时抛出。

异常会阻止程序继续执行,因此为了避免这种情况,需要捕获并单独处理错误。这个过程称为异常处理,程序员通过异常处理来处理运行时可能发生的错误。

异常处理语法

异常处理在代码块级别进行。一旦代码块中发生异常,控制权将离开该代码块的执行部分,并在代码块的异常处理部分中处理该异常。异常处理完毕后,控制权不能返回到同一代码块的执行部分。

下图展示了执行部分和异常处理部分如何在同一个 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 预定义了一些常见的异常。每个预定义的异常都有一个唯一的名称和错误编号,所有这些异常都在 STANDARD 包中声明。 Oracle在代码中,您可以直接使用这些预定义的异常名称来处理相应的错误。其中许多异常名称直接映射到常见的异常类型。 SQL 你每天都会遇到的错误。

以下是一些预先设定的例外情况:

特殊课程 误差 Code 异常原因
访问空值 ORA-06530 给未初始化对象的属性赋值
未找到案例 ORA-06592 CASE 语句中的所有 WHEN 子句均未满足,且未指定 ELSE 子句。
集合 _ 为 _ NULL ORA-06531 对未初始化的集合使用集合方法(EXISTS 除外)或访问集合属性
光标已打开 ORA-06511 尝试打开 光标 已经开放
索引上的重复值 ORA-00001 在受唯一索引约束的数据库列中存储重复值
无效光标 ORA-01001 非法光标操作,例如关闭未打开的光标
无效号码 ORA-01722 由于数字字符无效,字符到数字的转换失败。
没有找到数据 ORA-01403 包含 INTO 子句的 SELECT 语句不会获取任何行
行不匹配 ORA-06504 游标变量数据类型与实际游标返回类型不兼容
SUBSCRIPT_BEYOND_COUNT (订阅次数超过) ORA-06533 通过大于集合大小的索引号来引用集合
订阅超出限制 ORA-06532 使用超出合法范围的索引号(例如 -1)引用集合
行数过多 ORA-01422 带有 INTO 子句的 SELECT 语句会返回多行数据。
值错误 ORA-06502 算术错误或大小约束错误(例如,赋值大于变量大小的值)
零除法 ORA-01476 除以零

用户定义异常

除了上述预定义的异常之外,程序员还可以创建和处理自定义异常。自定义异常在子程序级别的声明部分创建,并且仅在该子程序内部可见。在包规范中定义的异常是公共异常,在任何可以访问该包的地方都可见。

语法:在子程序级别

DECLARE
<exception_name> EXCEPTION;
BEGIN
<Execution block>
EXCEPTION
WHEN <exception_name> THEN
<Handler>
END;
  • 在上面的语法中,变量“exception_name”被定义为异常类型。
  • 然后,它可以像预定义异常一样使用。

语法:在包规范级别

CREATE PACKAGE <package_name>
 IS
<exception_name> EXCEPTION;
.
.
END <package_name>;
  • 在上面的语法中,变量“exception_name”被定义为包规范中的异常类型。 。
  • 它可以在数据库中任何可以调用包名“package_name”的地方使用。

PL/SQL 引发异常

所有预定义的异常都会在发生相应错误时隐式抛出。但是,用户自定义的异常必须使用关键字 RAISE 显式抛出。RAISE 的用法如下所示。

如果在处理程序中单独使用 RAISE,它会将已引发的异常传播到父代码块。它只能在异常处理代码块中使用,如下所示。

下面的屏幕截图显示了单独使用 RAISE 来重新向封闭代码块抛出异常的情况:

使用裸 RAISE 将异常传播到父代码块

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

语法解释:

  • 在上面的语法中,关键字 RAISE 用于异常处理块内。
  • 每当程序遇到异常“exception_name”时,都会处理该异常并正常完成。
  • 处理程序中的关键字 RAISE 会将相同的异常传播到父程序。

注意: 当向父代码块抛出异常时,抛出的异常也必须在父代码块中可见;否则 Oracle 引发错误。

您还可以使用关键字 RAISE 后跟异常名称来引发特定的用户定义或预定义异常。这种形式既可用于执行部分,也可用于异常处理部分。

下面的屏幕截图显示了 RAISE 命令,后跟异常名称,用于引发特定异常:

使用 RAISE 语句,后跟异常名称,引发一个特定的命名异常。

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

语法解释:

  • 在上面的语法中,关键字 RAISE 用于执行部分,后面跟着异常“exception_name”。
  • 这会在运行时引发该特定异常,然后需要对其进行处理或进一步引发。

例如1: 在这个例子中,我们将看到:

  • 如何声明异常
  • 如何引发已声明的异常
  • 如何传播到主区块

下面的屏幕截图显示了完整的代码块,该代码块声明了 sample_exception,在嵌套代码块中引发了该异常,并将其传播到主代码块:

PL/SQL 示例:在嵌套块中声明并引发用户定义的异常

以下截图展示了相同示例的后续部分,其中主代码块最终捕获了传播的异常:

输出结果首先显示了嵌套代码块中捕获到的异常,然后显示了主代码块中捕获到的异常。

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;
/

Code 说明:

  • Code 第2行: 将变量“sample_exception”声明为 EXCEPTION 类型。
  • Code 第3行: 声明过程 nested_block。
  • Code 第6行: 打印语句“嵌套块内”。
  • Code 第7行: 打印语句“从嵌套块引发 sample_exception”。
  • Code 第8行: 使用“RAISE sample_exception”引发异常。
  • Code 第10行: 嵌套块中异常 sample_exception 的异常处理程序。
  • Code 第11行: 打印语句“嵌套代码块中捕获到异常。将异常抛出到主代码块”。
  • Code 第12行: 将异常抛出到主代码块(传播异常)。
  • Code 第15行: 打印语句“在主块内”。
  • Code 第16行: 打印语句“调用嵌套块”。
  • Code 第17行: 调用 nested_block 过程。
  • Code 第19行: 主块中 sample_exception 的异常处理程序。
  • Code 第20行: 打印语句“主代码块中捕获到异常”。

Exception 中需要注意的要点

  • 在函数中,异常要么应该返回一个值,要么应该进一步抛出异常;否则 Oracle 运行时抛出“函数返回时没有值”错误。
  • 交易控制声明 可以在异常处理块内发出。
  • SQLERRM 和 SQLCODE 是内置函数,分别返回异常消息和异常代码。
  • 如果未处理异常,则默认情况下该会话中的所有活动事务都会回滚。
  • RAISE_APPLICATION_ERROR (- ,可以使用 `)` 代替 `RAISE` 来引发带有自定义代码和消息的错误。错误代码必须介于 -20000 和 -20999 之间。

常见问题

PRAGMA EXCEPTION_INIT 将用户声明的异常名称与特定异常关联起来。 Oracle 错误编号。绑定后,您可以在 WHEN 子句中按名称捕获该 ORA 错误,而无需使用 WHEN OTHERS 并检查 SQLCODE。

SQLCODE 返回当前异常的数字错误代码,而 SQLERRM 返回其文本消息。两者都在异常处理程序中调用,通常是在 WHEN OTHERS 代码块中,用于记录或显示错误信息。

避免使用会默默吞噬所有错误的 WHEN OTHERS 语句。如果不记录 SQLCODE 和 SQLERRM 日志,或者使用 RAISE 重新抛出异常,就会隐藏错误。应该仅用于记录日志、清理异常,然后再传播异常。

RAISE_APPLICATION_ERROR 接受介于 -20000 和 -20999 之间的错误编号以及最大 2048 字节的消息。它会停止程序执行,并向调用应用程序返回自定义错误,从而使用户定义的条件看起来像一个原生错误。 Oracle 错误。

不在同一个代码块中——一旦控制流转移到 EXCEPTION 部分,该代码块就会结束。要继续执行,请将存在风险的语句包装在内部的 BEGIN…EXCEPTION…END 代码块中;内部代码块处理完错误后,外部代码块会继续执行。

错误是指代码中的任何运行时或编译问题。异常是 PL/SQL 运行时机制,它表示运行时错误,并将控制权转移到 EXCEPTION 部分,以便程序可以响应错误而不是突然终止。

是的。AI 助手和机器学习代码审查员会生成异常处理块,建议捕获哪些预定义的异常,并标记缺少处理程序的代码路径。开发人员在部署前仍然需要确认逻辑和错误信息。

GitHub 副驾驶 它能根据描述意图的简短注释自动补全 WHEN 子句、RAISE_APPLICATION_ERROR 调用和完整的 EXCEPTION 代码块。这加快了例程处理程序的编码速度,但生成的错误代码和消息需要审核以确保准确性。

总结一下这篇文章: