一、异常类型分类
类型 | 说明 | 示例 |
---|---|---|
内置预定义异常 | Oracle已命名异常(如NO_DATA_FOUND) | 查询无数据时触发 |
内置非预定义异常 | 未命名的Oracle错误(需用PRAGMA EXCEPTION_INIT关联) | ORA-02290(违反检查约束) |
自定义异常 | 用户定义的业务逻辑异常 | 数据校验失败时手动抛出 |
二、异常处理语法
1. 基本结构
DECLARE
-- 声明自定义异常
custom_exception EXCEPTION;
-- 绑定错误代码到非预定义异常
PRAGMA EXCEPTION_INIT(custom_exception, -20001);
BEGIN
-- 业务逻辑
IF 条件 THEN
RAISE custom_exception; -- 手动抛出异常
END IF;
EXCEPTION
WHEN custom_exception THEN
DBMS_OUTPUT.PUT_LINE('自定义异常触发');
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('数据未找到');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('错误代码: ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('错误信息: ' || SQLERRM);
END;
三、异常语法
1. 自定义异常
步骤说明:
①声明异常:
DECLARE
invalid_salary EXCEPTION;
②关联错误代码(可选):
PRAGMA EXCEPTION_INIT(invalid_salary, -20001);
③抛出异常:
IF salary < 0 THEN
RAISE invalid_salary;
END IF;
④捕获处理:
EXCEPTION
WHEN invalid_salary THEN
DBMS_OUTPUT.PUT_LINE('薪资不能为负数');
2. 内置异常处理
常用预定义异常:
NO_DATA_FOUND: SELECT未找到数据
TOO_MANY_ROWS: SELECT返回多行数据
ZERO_DIVIDE: 除数为零
DUP_VAL_ON_INDEX: 唯一索引冲突
-- 示例:
BEGIN
SELECT * INTO emp_rec FROM emp WHERE emp_id = 999;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('员工ID不存在');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('返回多行数据');
END;
3. 嵌套异常处理
结构示例:
DECLARE
outer_exception EXCEPTION;
BEGIN
<<inner_block>>
DECLARE
inner_exception EXCEPTION;
BEGIN
RAISE inner_exception;
EXCEPTION
WHEN inner_exception THEN
DBMS_OUTPUT.PUT_LINE('内部异常已处理');
RAISE outer_exception; -- 抛出到外层
END inner_block;
EXCEPTION
WHEN outer_exception THEN
DBMS_OUTPUT.PUT_LINE('外部捕获到异常');
END;
详细实例:
DECLARE
-- 定义变量和异常
v_employee_id employees.employee_id%TYPE := 100;
v_salary employees.salary%TYPE;
e_negative_salary EXCEPTION; -- 自定义异常:工资为负
PRAGMA EXCEPTION_INIT(e_negative_salary, -20001); -- 绑定错误代码
BEGIN
-- 外层块
BEGIN -- 内层块开始
-- 尝试获取员工工资
SELECT salary INTO v_salary
FROM employees
WHERE employee_id = v_employee_id;
-- 模拟业务逻辑:检查工资有效性
IF v_salary < 0 THEN
RAISE_APPLICATION_ERROR(-20001, '工资不能为负数'); -- 触发自定义异常
END IF;
DBMS_OUTPUT.PUT_LINE('内层块:工资处理成功。');
EXCEPTION -- 内层异常处理
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('内层块:员工ID ' || v_employee_id || ' 不存在。');
RAISE; -- 重新抛出到外层
WHEN e_negative_salary THEN
DBMS_OUTPUT.PUT_LINE('内层块:异常 - ' || SQLERRM);
-- 此处可添加恢复逻辑,如设置默认工资
v_salary := 0;
DBMS_OUTPUT.PUT_LINE('内层块:工资已重置为0。');
END; -- 内层块结束
-- 外层业务逻辑(内层未异常终止时执行)
DBMS_OUTPUT.PUT_LINE('外层块:更新后工资为 ' || v_salary);
EXCEPTION -- 外层异常处理
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('外层块:错误 - 指定员工不存在。');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('外层块:未知错误 - ' || SQLERRM);
ROLLBACK; -- 示例事务回滚
END;
嵌套结构:
- 内层块: 处理具体数据库操作(如查询、业务验证),捕获NO_DATA_FOUND(预定义)和自定义异常。
- 外层块: 处理内层未处理的异常,执行后续业务逻辑或全局错误处理。
异常传播:
- 内层处理并解决: 如e_negative_salary异常被捕获后重置工资,程序继续执行外层代码。
- 内层重新抛出: NO_DATA_FOUND在内层处理后通过RAISE传递到外层,触发外层对应处理程序。
自定义异常:
- 使用RAISE_APPLICATION_ERROR抛出并关联错误代码,PRAGMA
EXCEPTION_INIT将自定义异常绑定到特定错误号。
事务控制:
- 外层异常处理中可包含ROLLBACK,确保数据一致性(根据实际业务需求调整)。
嵌套规则:
- 内部块未处理的异常会自动传递到外层块
- 使用RAISE可手动将异常传递到上层
四、其他处理
1. 错误日志记录
EXCEPTION
WHEN OTHERS THEN
INSERT INTO error_log (code, message, time)
VALUES (SQLCODE, SQLERRM, SYSDATE);
COMMIT;
RAISE; -- 继续向上层传递异常
END;
2. 动态错误消息
RAISE_APPLICATION_ERROR(-20001, '订单 ' || order_id || ' 状态无效');
3. 异常传播控制
BEGIN
-- 业务逻辑
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -2290 THEN
DBMS_OUTPUT.PUT_LINE('约束违反');
ELSE
RAISE; -- 继续传播未明确处理的异常
END IF;
END;
五、总结
优先级处理:
EXCEPTION
WHEN NO_DATA_FOUND THEN ... -- 特定异常在前
WHEN OTHERS THEN ... -- 通用处理在后
事务控制:
BEGIN
SAVEPOINT sp1;
-- DML操作
EXCEPTION
WHEN OTHERS THEN
ROLLBACK TO sp1;
RAISE;
END;
错误代码规范:
- 自定义错误代码范围:-20000 到 -20999
- 统一错误消息格式
通过合理使用异常处理机制,可显著提升Oracle程序的健壮性。建议在复杂业务逻辑中优先定义清晰的异常处理策略。