在Oracle数据库中,异常处理主要通过PL/SQL(过程化SQL)实现,用于捕获和处理运行时错误。 ## 一、语法结构 ```sql DECLARE -- 声明部分(可选) BEGIN -- 执行逻辑 EXCEPTION WHEN exception1 THEN -- 处理异常1的代码 WHEN exception2 THEN -- 处理异常2的代码 WHEN OTHERS THEN -- 可选,捕获其他未明确处理的异常 -- 默认异常处理 END; ``` **参数说明**: - `WHEN exception_name THEN`:匹配特定异常类型。 - `OTHERS`:可选参数,用于捕获所有未被明确处理的异常(需谨慎使用,建议最后声明)。 ## 二、异常类型 #### 1、预定义异常 由Oracle自动定义 | 异常名称 | 错误码 | 说明 | | ------------------ | --------- | --------------------------- | | `NO_DATA_FOUND` | ORA-01403 | `SELECT...INTO`未返回数据 | | `TOO_MANY_ROWS` | ORA-01422 | `SELECT...INTO`返回多行数据 | | `ZERO_DIVIDE` | ORA-01476 | 除数为零 | | `DUP_VAL_ON_INDEX` | ORA-00001 | 违反唯一索引约束 | | `INVALID_CURSOR` | ORA-01001 | 非法游标操作 | | `VALUE_ERROR` | ORA-06502 | 数据类型转换错误 | | `STORAGE_ERROR` | ORA-06500 | 内存溢出 | **处理示例** 处理`ZERO_DIVIDE`异常 ```sql BEGIN DBMS_OUTPUT.PUT_LINE(10 / 0); -- 触发除零错误 EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('错误:除数不能为零!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('未知错误:' || SQLCODE); -- SQLCODE代表错误码 END; ``` #### 2、用户自定义异常 需显式声明并触发,适用于业务逻辑错误 ```sql DECLARE salary_too_high EXCEPTION; -- 声明异常 emp_salary NUMBER := 100000; BEGIN IF emp_salary > 50000 THEN RAISE salary_too_high; -- 主动抛出异常 END IF; EXCEPTION WHEN salary_too_high THEN DBMS_OUTPUT.PUT_LINE('错误:工资超过上限!'); END; ``` #### 3、非预定义异常 将Oracle错误码与自定义异常名称绑定,用于处理未预定义的数据库错误 ```sql DECLARE foreign_key_violation EXCEPTION; PRAGMA EXCEPTION_INIT(foreign_key_violation, -2292); -- 绑定错误码ORA-02292 BEGIN DELETE FROM departments WHERE department_id = 60; -- 若被外键引用则触发 EXCEPTION WHEN foreign_key_violation THEN DBMS_OUTPUT.PUT_LINE('错误:存在关联数据,无法删除!'); END; ``` ## 三、辅助函数 `SQLCODE`:返回当前错误的代码。 `SQLERRM`:返回当前错误的文本信息。 ```sql EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('错误码:' || SQLCODE || ',错误信息:' || SQLERRM); ``` ## 四、嵌套代码块 作用类似于`try-catch`,用于部分可能会抛出异常的代码。 ```sql BEGIN DBMS_OUTPUT.PUT_LINE('1. 外层开始'); BEGIN -- 内层块 DBMS_OUTPUT.PUT_LINE('2. 内层开始'); RAISE ZERO_DIVIDE; -- 触发异常 DBMS_OUTPUT.PUT_LINE('3. 内层异常后 - 不会执行'); EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('4. 内层处理异常'); END; DBMS_OUTPUT.PUT_LINE('5. 外层继续执行'); -- 这里会执行 EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('6. 外层异常处理'); END; ``` **输出结果** ```sql 1. 外层开始 2. 内层开始 4. 内层处理异常 5. 外层继续执行 ``` ## 五、动态SQL执行 #### 1、EXECUTE IMMEDIATE ##### 语法 ```sql EXECUTE IMMEDIATE dynamic_sql_string [INTO {variable1, variable2... | record}] [USING [IN | OUT | IN OUT] bind_argument1, bind_argument2...]; ``` **参数说明**: - `dynamic_sql_string`:动态SQL字符串(必选) - `INTO`:可选,用于存储查询结果(仅用于返回单行的查询) - `USING`:可选,绑定参数(避免SQL注入,提高性能) ##### 主要作用 - 执行动态拼接的 DDL(如 `CREATE`, `ALTER`)、DML(如 `INSERT`, `UPDATE`)语句 - 执行动态查询(需结合 `INTO` 子句) - 调用动态 PL/SQL 块 #### 2、示例 ##### 执行带参数的 DML ```sql DECLARE v_sql VARCHAR2(200); v_emp_id NUMBER := 100; v_salary NUMBER := 5000; BEGIN v_sql := 'UPDATE employees SET salary = :sal WHERE employee_id = :id'; EXECUTE IMMEDIATE v_sql USING v_salary, v_emp_id; END; ``` ##### 动态查询并返回值 ```sql DECLARE v_emp_name VARCHAR2(100); v_emp_id NUMBER := 100; BEGIN EXECUTE IMMEDIATE 'SELECT first_name FROM employees WHERE employee_id = :id' INTO v_emp_name USING v_emp_id; DBMS_OUTPUT.PUT_LINE('员工姓名:' || v_emp_name); END; ``` ##### 使用动态SQL的返回码 ```sql DECLARE v_rows NUMBER; BEGIN EXECUTE IMMEDIATE 'UPDATE emp SET salary = salary * 1.1' RETURNING ROWCOUNT INTO v_rows; DBMS_OUTPUT.PUT_LINE('更新了 ' || v_rows || ' 行记录'); END; ``` #### 3、异常处理 **动态 SQL**的语法错误在**运行时**才会触发异常 ##### 异常类型 | 异常场景 | 异常类型 | 示例 | | -------------- | ----------------------- | ------------------------------------------ | | 表不存在 | `ORA-00942` | `EXECUTE IMMEDIATE 'DROP TABLE non_exist'` | | 语法错误 | `ORA-00900`-`ORA-00999` | `EXECUTE IMMEDIATE 'SELEC * FROM dual'` | | 绑定参数不匹配 | `ORA-01008` | 参数数量/类型不匹配 | | 权限不足 | `ORA-01031` | 无删除表权限 | ##### 处理示例 ```sql DECLARE v_table_name VARCHAR2(30) := 'NON_EXISTENT_TABLE'; BEGIN EXECUTE IMMEDIATE 'DROP TABLE ' || v_table_name; EXCEPTION WHEN OTHERS THEN -- 需要根据SQLCODE进行具体判断 IF SQLCODE = -942 THEN -- 表不存在异常 DBMS_OUTPUT.PUT_LINE('表不存在,无需删除'); ELSIF SQLCODE = -1031 THEN -- 权限不足 DBMS_OUTPUT.PUT_LINE('权限不足,无法删除表'); ELSE DBMS_OUTPUT.PUT_LINE('其他错误:' || SQLERRM); RAISE; -- 重新抛出异常 END IF; END; ``` Loading... 在Oracle数据库中,异常处理主要通过PL/SQL(过程化SQL)实现,用于捕获和处理运行时错误。 ## 一、语法结构 ```sql DECLARE -- 声明部分(可选) BEGIN -- 执行逻辑 EXCEPTION WHEN exception1 THEN -- 处理异常1的代码 WHEN exception2 THEN -- 处理异常2的代码 WHEN OTHERS THEN -- 可选,捕获其他未明确处理的异常 -- 默认异常处理 END; ``` **参数说明**: - `WHEN exception_name THEN`:匹配特定异常类型。 - `OTHERS`:可选参数,用于捕获所有未被明确处理的异常(需谨慎使用,建议最后声明)。 ## 二、异常类型 #### 1、预定义异常 由Oracle自动定义 | 异常名称 | 错误码 | 说明 | | ------------------ | --------- | --------------------------- | | `NO_DATA_FOUND` | ORA-01403 | `SELECT...INTO`未返回数据 | | `TOO_MANY_ROWS` | ORA-01422 | `SELECT...INTO`返回多行数据 | | `ZERO_DIVIDE` | ORA-01476 | 除数为零 | | `DUP_VAL_ON_INDEX` | ORA-00001 | 违反唯一索引约束 | | `INVALID_CURSOR` | ORA-01001 | 非法游标操作 | | `VALUE_ERROR` | ORA-06502 | 数据类型转换错误 | | `STORAGE_ERROR` | ORA-06500 | 内存溢出 | **处理示例** 处理`ZERO_DIVIDE`异常 ```sql BEGIN DBMS_OUTPUT.PUT_LINE(10 / 0); -- 触发除零错误 EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('错误:除数不能为零!'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('未知错误:' || SQLCODE); -- SQLCODE代表错误码 END; ``` #### 2、用户自定义异常 需显式声明并触发,适用于业务逻辑错误 ```sql DECLARE salary_too_high EXCEPTION; -- 声明异常 emp_salary NUMBER := 100000; BEGIN IF emp_salary > 50000 THEN RAISE salary_too_high; -- 主动抛出异常 END IF; EXCEPTION WHEN salary_too_high THEN DBMS_OUTPUT.PUT_LINE('错误:工资超过上限!'); END; ``` #### 3、非预定义异常 将Oracle错误码与自定义异常名称绑定,用于处理未预定义的数据库错误 ```sql DECLARE foreign_key_violation EXCEPTION; PRAGMA EXCEPTION_INIT(foreign_key_violation, -2292); -- 绑定错误码ORA-02292 BEGIN DELETE FROM departments WHERE department_id = 60; -- 若被外键引用则触发 EXCEPTION WHEN foreign_key_violation THEN DBMS_OUTPUT.PUT_LINE('错误:存在关联数据,无法删除!'); END; ``` ## 三、辅助函数 `SQLCODE`:返回当前错误的代码。 `SQLERRM`:返回当前错误的文本信息。 ```sql EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('错误码:' || SQLCODE || ',错误信息:' || SQLERRM); ``` ## 四、嵌套代码块 作用类似于`try-catch`,用于部分可能会抛出异常的代码。 ```sql BEGIN DBMS_OUTPUT.PUT_LINE('1. 外层开始'); BEGIN -- 内层块 DBMS_OUTPUT.PUT_LINE('2. 内层开始'); RAISE ZERO_DIVIDE; -- 触发异常 DBMS_OUTPUT.PUT_LINE('3. 内层异常后 - 不会执行'); EXCEPTION WHEN ZERO_DIVIDE THEN DBMS_OUTPUT.PUT_LINE('4. 内层处理异常'); END; DBMS_OUTPUT.PUT_LINE('5. 外层继续执行'); -- 这里会执行 EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('6. 外层异常处理'); END; ``` **输出结果** ```sql 1. 外层开始 2. 内层开始 4. 内层处理异常 5. 外层继续执行 ``` ## 五、动态SQL执行 #### 1、EXECUTE IMMEDIATE ##### 语法 ```sql EXECUTE IMMEDIATE dynamic_sql_string [INTO {variable1, variable2... | record}] [USING [IN | OUT | IN OUT] bind_argument1, bind_argument2...]; ``` **参数说明**: - `dynamic_sql_string`:动态SQL字符串(必选) - `INTO`:可选,用于存储查询结果(仅用于返回单行的查询) - `USING`:可选,绑定参数(避免SQL注入,提高性能) ##### 主要作用 - 执行动态拼接的 DDL(如 `CREATE`, `ALTER`)、DML(如 `INSERT`, `UPDATE`)语句 - 执行动态查询(需结合 `INTO` 子句) - 调用动态 PL/SQL 块 #### 2、示例 ##### 执行带参数的 DML ```sql DECLARE v_sql VARCHAR2(200); v_emp_id NUMBER := 100; v_salary NUMBER := 5000; BEGIN v_sql := 'UPDATE employees SET salary = :sal WHERE employee_id = :id'; EXECUTE IMMEDIATE v_sql USING v_salary, v_emp_id; END; ``` ##### 动态查询并返回值 ```sql DECLARE v_emp_name VARCHAR2(100); v_emp_id NUMBER := 100; BEGIN EXECUTE IMMEDIATE 'SELECT first_name FROM employees WHERE employee_id = :id' INTO v_emp_name USING v_emp_id; DBMS_OUTPUT.PUT_LINE('员工姓名:' || v_emp_name); END; ``` ##### 使用动态SQL的返回码 ```sql DECLARE v_rows NUMBER; BEGIN EXECUTE IMMEDIATE 'UPDATE emp SET salary = salary * 1.1' RETURNING ROWCOUNT INTO v_rows; DBMS_OUTPUT.PUT_LINE('更新了 ' || v_rows || ' 行记录'); END; ``` #### 3、异常处理 **动态 SQL**的语法错误在**运行时**才会触发异常 ##### 异常类型 | 异常场景 | 异常类型 | 示例 | | -------------- | ----------------------- | ------------------------------------------ | | 表不存在 | `ORA-00942` | `EXECUTE IMMEDIATE 'DROP TABLE non_exist'` | | 语法错误 | `ORA-00900`-`ORA-00999` | `EXECUTE IMMEDIATE 'SELEC * FROM dual'` | | 绑定参数不匹配 | `ORA-01008` | 参数数量/类型不匹配 | | 权限不足 | `ORA-01031` | 无删除表权限 | ##### 处理示例 ```sql DECLARE v_table_name VARCHAR2(30) := 'NON_EXISTENT_TABLE'; BEGIN EXECUTE IMMEDIATE 'DROP TABLE ' || v_table_name; EXCEPTION WHEN OTHERS THEN -- 需要根据SQLCODE进行具体判断 IF SQLCODE = -942 THEN -- 表不存在异常 DBMS_OUTPUT.PUT_LINE('表不存在,无需删除'); ELSIF SQLCODE = -1031 THEN -- 权限不足 DBMS_OUTPUT.PUT_LINE('权限不足,无法删除表'); ELSE DBMS_OUTPUT.PUT_LINE('其他错误:' || SQLERRM); RAISE; -- 重新抛出异常 END IF; END; ``` 最后修改:2025 年 12 月 05 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 👆请简单粗暴地爱我。