常见预定义异常
- NO_DATA_FOUND:SELECT INTO 没查到数据,或引用集合中不存在的元素
- TOO_MANY_ROWS:SELECT INTO 返回多行
- VALUE_ERROR:类型转换或截断失败
- INVALID_NUMBER:字符串转数字失败
- DUP_VAL_ON_INDEX:唯一约束冲突
- 注意:SQL%ROWCOUNT = 0 不会抛 NO_DATA_FOUND,UPDATE 没匹配到行不是异常
DECLARE
v_sal employees.salary%TYPE;
BEGIN
SELECT salary INTO v_sal FROM employees WHERE department_id = 60;
DBMS_OUTPUT.PUT_LINE(v_sal);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('查询没有返回任何行');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('查询返回了多行,SELECT INTO 只能接一行');
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('除数不能为零');
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE('违反唯一约束');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('未预期错误: ' || SQLCODE || ' - ' || SQLERRM);
END;
/只作示例阅读:5 行写入限制下不执行任意 PL/SQL、循环写入或动态 SQL;只开放字面量 DBMS_OUTPUT 打印。请在独立学习数据库中练习。
自定义异常
DECLARE
-- 方式一:自己声明,自己 RAISE
e_salary_too_low EXCEPTION;
-- 方式二:绑定到 Oracle 错误号
e_child_found EXCEPTION;
PRAGMA EXCEPTION_INIT(e_child_found, -2292); -- 违反外键约束
v_sal NUMBER;
BEGIN
SELECT salary INTO v_sal FROM employees WHERE employee_id = 100;
IF v_sal < 5000 THEN
RAISE e_salary_too_low;
END IF;
DELETE FROM departments WHERE department_id = 60; -- 有子记录会触发 -2292
EXCEPTION
WHEN e_salary_too_low THEN
DBMS_OUTPUT.PUT_LINE('工资低于下限,拒绝处理');
WHEN e_child_found THEN
DBMS_OUTPUT.PUT_LINE('该部门下还有员工,不能删除');
END;
/只作示例阅读:5 行写入限制下不执行任意 PL/SQL、循环写入或动态 SQL;只开放字面量 DBMS_OUTPUT 打印。请在独立学习数据库中练习。
把错误抛给调用方
CREATE OR REPLACE PROCEDURE check_salary (p_sal IN NUMBER) IS
BEGIN
IF p_sal IS NULL THEN
RAISE_APPLICATION_ERROR(-20010, '工资不能为空');
END IF;
IF p_sal < 1000 THEN
RAISE_APPLICATION_ERROR(-20011, '工资不能低于 1000');
END IF;
END check_salary;
/
-- 调用方看到的就是自定义信息
BEGIN
check_salary(500);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE || ' -> ' || SQLERRM);
END;
/错误号必须在 -20000 到 -20999 之间,这是 Oracle 留给应用自定义错误的区间。
只作示例阅读:5 行写入限制下只开放单独创建普通空表;CTAS、删表、清空表、改结构、触发器和存储程序不允许执行。请在独立学习数据库中练习。
WHEN OTHERS 的正确姿势
- WHEN OTHERS 必须放在最后,它是兜底分支
- 不要在 WHEN OTHERS 里什么都不做——这会把错误吞掉,是排查噩梦的根源
- 至少要记录 SQLCODE 和 SQLERRM,或调用 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE 拿到出错行号
- 如果不打算处理,就 RAISE; 原样抛出,让上层决定
- 异常会被传播到调用者;过程中未处理的异常会回滚该过程内的修改
BEGIN
-- 可能出错的逻辑
UPDATE employees SET salary = salary / 0 WHERE employee_id = 100;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('错误码 : ' || SQLCODE);
DBMS_OUTPUT.PUT_LINE('错误信息: ' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('回溯 : ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
RAISE;
-- 记录完再抛出去,不吞错误
END;
/只作示例阅读:5 行写入限制下不执行任意 PL/SQL、循环写入或动态 SQL;只开放字面量 DBMS_OUTPUT 打印。请在独立学习数据库中练习。