PL/SQL 异常处理

目录

一、命名的系统异常

1.常见命名的系统异常

2.预定义的系统异常数量以及描述

3.处理命名的系统异常的一般步骤

二、程序员定义的异常

1.概念

2.自定义异常的定义与抛出

3.自定义异常的处理

三、未命名的系统异常

1.概述

2.处理未命名的系统异常的方法

3.使用场景

四、未命名的程序员自定义的异常

1.概述

2.作用

[3. 使用方法及示例](#3. 使用方法及示例)

未命名的程序员自定义异常与程序员定义异常的区别

1.声明与抛出

2.错误代码和信息

3.使用场景


引言

在设计PL/SQL程序时,经常会发生这样或者那样的错误,异常处理就是针对错误进行处理的程序段,Oracle中的异常处理分为系统预定义异常处理和自定义异常处理两部分。

异常的类型大概分为四类:

1.命名的系统异常

2.程序员定义的异常

3.未命名的系统异常

4.未命名的程序员定义的异常

一、命名的系统异常

命名的系统异常是Oracle数据库为了处理常见的错误情况而预定的一组异常。它们由Oracle数据库在特定条件下自动抛出,开发者就可以在PL/SQL程序中通过异常机制来捕获和处理这些异常。

1.常见命名的系统异常

NO_DATA_FOUND

错误代码:ORA-01403

错误描述:当执行SELECT INTO语句时,如果没有返回任何数据,就会抛出此异常。

示例:

sql 复制代码
DECLARE
    v_name varchar(10);
BEGIN
    SELECT s_name INTO v_name from Students WHERE s_id = '001';
EXCEPTION
    WHEN NOT_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('未找到编号为001的学生');
END;

TOO_MANY_ROWS

错误代码:ORA-01422

描述:当执行SELECT INTO语句时,如果返回多行,则会抛出异常。

示例:

sql 复制代码
DECLARE
    v_name varchar(10);
BEGIN
    SELECT s_name INTO v_name from Students WHERE s_id = '001';
EXCEPTION
    WHEN TOO_MANY_ROWS THEN
        DBMS_OUTPUT.PUT_LINE('有多个编号为001的学生');
END;

2.预定义的系统异常数量以及描述

**数量:**大约24种。

异常名称 异常编号(SQLCODE) 描述
ACCESS_INTO_NULL -6530 尝试为未初始化的对象属性赋值时触发。
CASE_NOT_FOUND -6592 CASE语句中没有匹配的WHEN子句且没有设置ELSE子句时触发。
COLLECTION_IS_NULL -6531 尝试使用未初始化的集合元素时触发。
CURSOR_ALREADY_OPEN -6511 尝试打开一个已经打开的游标时触发。
DUP_VAL_ON_INDEX -1 尝试插入或更新数据时,唯一索引对应的列上有重复的值时触发。
INVALID_CURSOR -1001 尝试使用无效的游标时触发。
INVALID_NUMBER -1722 尝试将字符转换为数字失败时触发。
LOGIN_DENIED -1017 尝试使用无效的用户名或密码登录Oracle数据库时触发。
NO_DATA_FOUND -1403 SELECT INTO语句未返回任何数据时触发。
NOT_LOGGED_ON -1012 尝试在未登录的情况下访问Oracle数据库时触发。
PROGRAM_ERROR -6501 PL/SQL内部错误,可能需要重装数据字典或PL/SQL系统包时触发。
ROWTYPE_MISMATCH -6504 宿主游标变量与PL/SQL游标变量的返回类型不兼容时触发。
SELF_IS_NULL -(无固定编号) 使用对象类型时,在NULL对象上调用对象方法时触发。
STORAGE_ERROR -6500 运行PL/SQL时,超出内存空间或内存被破坏时触发。
SUBSCRIPT_BEYOND_COUNT -6533 尝试访问嵌套表或VARRAY时,元素下标超出最大值时触发。
SUBSCRIPT_OUTSIDE_LIMIT -6532 使用嵌套表或VARRAY时,元素下标为负数时触发。
SYS_INVALID_ID -1410 尝试使用无效的ROWID字符串时触发。
TIMEOUT_ON_RESOURCE -51 等待资源时超时触发。
TOO_MANY_ROWS -1422 SELECT INTO语句返回多行时触发。
VALUE_ERROR -6502 赋值时,变量长度不足以容纳实际数据时触发。
ZERO_DIVIDE -1476 尝试执行除数为零的除法运算时触发。
CURSOR_IS_OPEN -(非标准,示例) 尝试在游标已打开的情况下进行非法操作(如重复打开)时触发。
COLLECTION_IS_LOCKED -(非标准,示例) 尝试访问已锁定的集合元素时触发。
INVALID_TRANSACTION -(非标准,示例) 尝试执行无效的事务操作时触发。
NETWORK_ERROR -(非标准,示例) 网络通信错误时触发。

说明

  1. 异常编号(SQLCODE) :Oracle数据库为每种异常分配了一个唯一的错误编号(SQLCODE),用于标识该异常。在异常处理中,可以通过捕获异常编号来判断具体的错误类型。

  2. 异常名称:Oracle数据库为每种异常提供了一个名称,便于开发人员在代码中引用和处理。

  3. 描述:简要说明了触发每种异常的条件和场景。

  4. 非标准异常 :上述列表中最后几种异常(如CURSOR_IS_OPENCOLLECTION_IS_LOCKEDINVALID_TRANSACTIONNETWORK_ERROR)是示例性的,用于说明可能的异常类型,并非Oracle数据库标准预定义的异常。实际开发中,这些异常可能需要开发人员根据业务需求自定义。

  5. 异常处理 :在PL/SQL程序中,可以通过EXCEPTION块来捕获和处理这些预定义异常,以确保程序的健壮性和可靠性。

3.处理命名的系统异常的一般步骤

捕获异常 :在PL/SQL程序中,使用EXCEPTION块来捕获可能抛出的命名系统异常。

处理异常:在捕获到异常后,执行相应的处理逻辑,如输出错误信息、记录日志、回滚事务等。

继续执行或者退出程序:根据需求,决定是继续执行后续代码还是退出程序。一旦进EXCEPTION块,后续的代码(指在BEGIN ... END块中EXCEPTION部分之后的代码)将不会被执行。(这过程简直和Java一摸一样)

示例:

sql 复制代码
DECLARE
    v_error_occurred BOOLEAN := FALSE;--变量v_error_occurred用于跟踪是否发生了错误,以便在后续代码中采取适当的操作。
BEGIN
    -- 尝试执行一些可能引发异常的代码
    BEGIN
        -- 这里是可能抛出异常的代码
        RAISE NO_DATA_FOUND; -- 示例异常
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            DBMS_OUTPUT.PUT_LINE('No data found, continuing with the next step.');
            v_error_occurred := TRUE; -- 标记错误已发生
    END;
    
    -- 后续的代码,无论是否发生异常都会执行
    IF v_error_occurred THEN
        DBMS_OUTPUT.PUT_LINE('An error occurred, but we are continuing with the process.');
    END IF;
    
    DBMS_OUTPUT.PUT_LINE('This is the subsequent code that will always be executed.');
    
    -- 更多的后续代码...
END;

二、程序员定义的异常

1.概念

自定义异常是程序员根据业务逻辑需求,在PL/SQL程序中显示声明的异常。当程序中出现特定条件时,程序员可以显式地抛出这些自定义异常,并在异常处理部分(EXCEPTION)进行捕获和处理。

2.自定义异常的定义与抛出

定义异常 :在块声明部分,使用DECLARE关键字声明异常

sql 复制代码
DECLARE
    my_custom_exception EXCEPTION;

抛出异常 :在程序执行部分,使用RAISE语句抛出自定义异常。

sql 复制代码
BEGIN
    IF condition THEN
        RAISE my_custom_exception;
    END IF

3.自定义异常的处理

捕获异常:在EXCEPTION块中,使用WHEN...TEHN语句捕获并处理自定义异常。

sql 复制代码
EXCEPTION   
    WHEN my_custom_exception THEN
        DBMS_OUTPUT.PUT_LINE('自定义异常被触发');
        --处理异常,记录日志,事务回滚等
END

与其他异常一起处理:在EXCEPTION块中,程序员可以捕获和处理多种异常,包括预定义异常和自定义异常。

sql 复制代码
EXCEPTION
	WHEN NO_DATA_FOUND THEN
		DBMS_OUTPUT.PUT_LINE('未找到数据');
	WHEN my_custom_exception THEN
		DBMS_OUTPUT.PUT_LINE('自定义异常被触发');
	WHEN OTHERS THEN
		DBMS_OUTPUT.PUT_LINE('发生其他异常: ' || SQLERRM);

OTHERS子句用于捕获和处理所有未显式捕获的异常。

三、未命名的系统异常

在PL/SQL中,未命名的系统异常是指那些由Oracle数据库系统内部产生的、没有预定义异常名称的错误。这些异常通常以错误代码(Oracle错误号)的形式出现,而没有与之直接对应的异常名。为了处理这些未命名的系统异常,PL/SQL提供了相应的机制和语法。

1.概述

未命名的系统异常通常是由于违反Oracle数据库的内部规则或系统限制而产生的。这些异常没有预定义的异常名称,但Oracle数据库为每个异常分配了一个唯一的错误代码(Oracle错误号)。例如,当尝试删除一个具有子记录的父表记录时,Oracle会抛出错误代码为-2292的未命名系统异常。

2.处理未命名的系统异常的方法

声明异常名称:使用EXCEPTION关键字声明一个自定义的异常名称。

sql 复制代码
DECLARE
    child_record_exception EXCEPTION

将异常名称与错误代码关联 :使用PRAGMA EXCEPTION_INT伪指令将自定义的异常名称与未命名的系统异常的错误代码关联起来。

sql 复制代码
PRAGMA EXCEPTION_INIT(child_record_exception, -2292);

在异常处理部分捕获异常

在PL/SQL块的异常处理部分,使用WHEN关键字捕获自定义的异常名称,并执行相应的处理逻辑。

sql 复制代码
BEGIN
    -- 执行可能导致异常的操作,例如删除具有子记录的父表记录
    DELETE FROM parent_table WHERE id = 1;
EXCEPTION
    WHEN child_record_exception THEN
        DBMS_OUTPUT.PUT_LINE('无法删除父记录,因为存在子记录。');
END;

完整示例

sql 复制代码
DECLARE
    child_record_exception EXCEPTION;
    PRAGMA EXCEPTION_INIT(child_record_exception, -2292);
BEGIN
    -- 尝试删除一个具有子记录的父表记录
    DELETE FROM departments WHERE department_id = 10;
EXCEPTION
    WHEN child_record_exception THEN
        DBMS_OUTPUT.PUT_LINE('无法删除父记录,因为存在子记录。');
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('发生其他异常:' || SQLERRM);
END;

3.使用场景

  1. 处理特定的系统错误 :Oracle数据库在运行时会产生各种系统错误,这些错误通常以错误代码的形式出现,而没有与之直接对应的异常名。例如,当尝试删除一个具有子记录的父表记录时,Oracle会抛出错误代码为-2292的系统错误。为了处理这种特定的系统错误,可以使用未命名的系统异常。

  2. 增强代码的可读性和可维护性 :通过为未命名的系统异常定义一个明确的异常名称,并使用PRAGMA EXCEPTION_INIT伪指令将其与错误代码关联起来,可以使代码更加清晰易懂。这样,在异常处理部分,就可以直接使用自定义的异常名称,而不需要记住复杂的错误代码。

  3. 实现更细粒度的异常处理 :在PL/SQL中,可以使用WHEN OTHERS子句来捕获所有未捕获的异常。然而,这种方式过于笼统,可能无法准确地定位问题。通过为特定的系统错误定义未命名的系统异常,可以实现更细粒度的异常处理,从而更好地定位问题并采取适当的解决措施。

四、未命名的程序员自定义的异常

1.概述

RAISE_APPLICATION_ERROR 在 Oracle PL/SQL 中用于引发用户自定义的异常。它主要用于在应用程序中主动抛出自定义的错误代码和错误消息,以便更好地处理异常情RAISE_APPLICATION_ERROR 引发的是用户自定义的异常。 这种异常不是由 Oracle 系统自动抛出的,而是由开发人员根据业务需求,在 PL/SQL 代码中显式抛出的。

2.作用

提供详细的错误信息RAISE_APPLICATION_ERROR 允许开发人员自定义错误代码和错误消息。这样,当异常被抛出时,调用程序可以接收到更详细、更有意义的错误信息,而不是仅仅依赖于 Oracle 的标准错误消息。

增强错误处理灵活性:通过引发自定义异常,开发人员可以更灵活地控制错误处理流程。例如,可以在不同的业务逻辑分支中抛出不同的异常,以便调用程序根据异常类型采取相应的处理措施。

提高程序的可维护性 :使用 RAISE_APPLICATION_ERROR 可以将错误处理逻辑与业务逻辑分离,使得程序更加清晰易读。同时,自定义的异常处理机制也便于后续的代码维护和升级。

3. 使用方法及示例

基本语法

sql 复制代码
RAISE_APPLICATION_ERROR(error_number,error_message [, kee_error]);
  • error_number:自定义的错误代码,必须是一个介于 -20000 和 -20999 之间的整数。这个范围内的错误代码不会与 Oracle 的保留错误代码冲突。

  • error_message:自定义的错误消息,是一个字符串。该消息的长度不应超过 2048 个字符。它将被传递给调用程序,以便调用程序了解异常的具体原因。

  • keep_errors (可选):这是一个布尔值参数,用于指示是否将当前的错误堆栈信息包含在异常中。在 Oracle 12c 及更高版本中引入。如果设置为 FALSE(默认值),则当前的错误信息会替换已有的错误堆栈。如果设置为 TRUE,则当前的错误信息会被添加到已有的错误堆栈中。

使用示例

假设我们有一个存储过程,用于检查员工的薪水是否低于某个最低值。如果薪水低于最低值,则使用 RAISE_APPLICATION_ERROR 抛出异常:

sql 复制代码
CREATE OR REPLACE PROCEDURE check_salary (p_emp_id IN NUMBER, p_min_salary IN NUMBER) IS
    v_salary NUMBER;
BEGIN
    -- 查询员工薪水
    SELECT salary INTO v_salary FROM employees WHERE employee_id = p_emp_id;

    -- 检查薪水是否低于最低值
    IF v_salary < p_min_salary THEN
        -- 抛出自定义异常
        RAISE_APPLICATION_ERROR(-20001, 'Employee salary is below the minimum required.');
    END IF;

EXCEPTION
    WHEN NO_DATA_FOUND THEN
        -- 处理未找到员工数据的情况
        RAISE_APPLICATION_ERROR(-20002, 'Employee ID not found.');
END;

未命名的程序员自定义异常与程序员定义异常的区别

1.声明与抛出

  • 自定义异常需要先声明,然后才能抛出。声明是在 PL/SQL 块的声明部分进行的,而抛出是在执行部分进行的。
  • RAISE_APPLICATION_ERROR 则不需要事先声明异常,它直接在执行部分抛出自定义的错误代码和消息。

2.错误代码和信息

  • 自定义异常本身不包含错误代码和消息,这些需要在抛出异常时通过其他方式(如注释或文档)进行说明。
  • RAISE_APPLICATION_ERROR 则直接包含了错误代码和消息,使得异常信息更加完整和直观。

3.使用场景

  • 自定义异常更适合用于处理那些与 Oracle 系统错误无关的业务逻辑错误,例如数据验证失败、业务规则违反等。
  • RAISE_APPLICATION_ERROR 则更适合用于处理那些需要自定义错误代码和消息的异常情况,例如当应用程序需要向用户显示特定的错误消息时。
相关推荐
博一波2 分钟前
Redis 集群:连锁银行的 “多网点智能协作系统”
数据库·redis·缓存
HashData酷克数据8 分钟前
官宣:Apache Cloudberry (Incubating) 2.0.0 发布!
数据库·开源·apache·cloudberry
秋难降8 分钟前
SQL 索引突然 “罢工”?快来看看为什么
数据库·后端·sql
TDengine (老段)38 分钟前
TDengine 时间函数 TODAY() 用户手册
大数据·数据库·物联网·oracle·时序数据库·tdengine·涛思数据
码界奇点1 小时前
KingbaseES一体化架构与多层防护体系如何保障企业级数据库的持续稳定与弹性扩展
数据库·架构·可用性测试
悟乙己1 小时前
数据科学家如何更好地展示自己的能力
大数据·数据库·数据科学家
皆过客,揽星河1 小时前
mysql进阶语法(视图)
数据库·sql·mysql·mysql基础语法·mysql进阶语法·视图创建修改删除
Lris-KK2 小时前
【Leetcode】高频SQL基础题--180.连续出现的数字
sql·leetcode
tuokuac2 小时前
Redis 的相关文件作用
数据库·redis·缓存
鹧鸪云光伏与储能软件开发3 小时前
投资储能项目能赚多少钱?小程序帮你测算
运维·数据库·小程序·光伏·光伏设计软件·光伏设计