异常处理可以让程序更“健壮”,品质更高,所以很重要!
好在PL/SQL的程序结构挺清晰,都是程式化的东西,没什么难理解的。
--代码12.11 Exception_int使用示例
DECLARE
e_missingnull EXCEPTION; --先声明一个异常
--PRAGMA时编译指令的声明,表示Exception_init编译指令将在编译时被处理而不是运行时
PRAGMA EXCEPTION_INIT (e_missingnull, -1400); --将该异常与-1400进行关联
BEGIN
INSERT INTO emp(empno)VALUES (NULL); --向emp表中不为空的列empno插入NULL值
COMMIT; --如果执行成功则使用COMMIT提交
EXCEPTION
WHEN e_missingnull THEN --如果失败则捕捉到命名的异常
DBMS_OUTPUT.put_line ('触发了ORA-1400错误!'||SQLERRM);
ROLLBACK;
END;
/
--代码12.12 Raise_Application_Error使用示例
CREATE OR REPLACE PROCEDURE registeremployee (
p_empno IN emp.empno%TYPE, --员工编号
p_ename IN emp.ename%TYPE, --员工名称
p_sal IN emp.sal%TYPE, --员工薪资
p_deptno IN emp.deptno%TYPE --部门编号
)
AS
v_empcount NUMBER;
BEGIN
IF p_empno IS NULL --如果员工编号为NULL则触发错误
THEN
raise_application_error (-20000, '员工编号不能为空'); --触发应用程序异常
ELSE
SELECT COUNT (*)
INTO v_empcount
FROM emp
WHERE empno = p_empno; --判断员工编号是否存在
IF v_empcount > 0 --如果员工编号已存在
THEN
raise_application_error (-20001,
'员工编号为:' || p_empno
|| '的员工已存在!'
); --触发应用程序异常
END IF;
END IF;
IF p_deptno IS NULL --如果部门编号为NULL
THEN
raise_application_error (-20002, '部门编号不能为空'); --触发应用程序异常
END IF;
INSERT INTO emp --向emp表中插入员工记录
(empno, ename, sal, deptno
)
VALUES (p_empno, p_ename, p_sal, p_deptno
);
EXCEPTION
WHEN OTHERS THEN --捕捉应用程序异常
raise_application_error (-20003,
'插入数据时出现错误!异常编码:'
|| SQLCODE
|| ' 异常描述 '
|| SQLERRM
);
END;
BEGIN
RegisterEmployee(7368,'李明',2000,40);
END;
select * from emp;
--代码12.14 异常处理示例(在一个处理语句块中中处理多个异常)
DECLARE
e_nocomm EXCEPTION; --自定义的异常
v_comm NUMBER (10, 2); --临时保存提成数据的变量
v_empno NUMBER (4) := &empno; --从绑定参数中获取员工信息
BEGIN
SELECT comm INTO v_comm FROM emp WHERE empno = v_empno; --查询并获取员工提成
IF v_comm IS NULL --如果没有提成
THEN
RAISE e_nocomm; --触发异常
END IF;
DBMS_OUTPUT.put_line ('员工'||v_empno||'的提成为:'||v_comm);
EXCEPTION
WHEN e_nocomm THEN --处理自定义异常
DBMS_OUTPUT.put_line ('选择的员工没有提成!');
WHEN NO_DATA_FOUND THEN --处理预定义异常
DBMS_OUTPUT.put_line ('没有找到任何数据');
WHEN OTHERS THEN --处理预定义异常
DBMS_OUTPUT.put_line ('任何其他未处理的异常');
END;
--代码12.16 使用SQLCODE和SQLERRM显示错误消息
DECLARE
e_nocomm EXCEPTION; --自定义的异常
v_comm NUMBER (10, 2); --临时保存提成数据的变量
v_empno NUMBER (4) := &empno; --从绑定参数中获取员工信息
BEGIN
SELECT comm INTO v_comm FROM emp WHERE empno = v_empno; --查询并获取员工提成
IF v_comm IS NULL --如果没有提成
THEN
RAISE e_nocomm; --触发异常
END IF;
EXCEPTION
WHEN e_nocomm THEN --OTHERS必须单独出现
DBMS_OUTPUT.put_line ('该员工无提成!');
WHEN OTHERS THEN --OTHERS必须单独出现
DBMS_OUTPUT.put_line ('错误编码:'||SQLCODE||' 错误消息:'||SQLERRM(100));
END;
select * from emp where comm is null;
--异常传递
DECLARE
e_outerexception EXCEPTION;
e_innerexception EXCEPTION;
e_threeexception EXCEPTION;
BEGIN
BEGIN
RAISE e_innerexception;
RAISE e_outerexception;
RAISE e_threeexception;
EXCEPTION
WHEN e_innerexception THEN
--异常处理代码
END;
EXCEPTION
WHEN e_outerexception THEN
--异常处理代码
END;
BEGIN
DECLARE
v_ename VARCHAR2(2):='ABC';
BEGIN
DBMS_OUTPUT.PUT_LINE(v_ename);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('产生了异常');
END;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('错误编号:'
||SQLCODE||' 错误消息:'
||SQLERRM);
END;
DECLARE
e_outerexception EXCEPTION;
e_innerexception EXCEPTION;
e_threeexception EXCEPTION;
BEGIN
BEGIN
RAISE e_innerexception;
RAISE e_outerexception;
RAISE e_threeexception;
EXCEPTION
WHEN e_innerexception THEN
RAISE e_outerexception;
WHEN e_outerexception THEN
--异常处理代码
WHEN OTHERS THEN
--异常处理代码
END;
EXCEPTION
WHEN e_outerexception THEN
--异常处理代码
END;
SELECT * FROM emp;
--代码12.17 重新抛出异常示例
DECLARE
e_nocomm EXCEPTION; --自定义的异常
v_comm NUMBER (10, 2); --临时保存提成数据的变量
v_empno NUMBER (4) := &empno; --从绑定参数中获取员工信息
BEGIN
SELECT comm INTO v_comm FROM emp WHERE empno = v_empno; --查询并获取员工提成
IF v_comm IS NULL --如果没有提成
THEN
RAISE e_nocomm; --触发异常
END IF;
EXCEPTION
WHEN OTHERS THEN --OTHERS必须单独出现
DBMS_OUTPUT.put_line ('错误编码:'||SQLCODE||' 错误消息:'||SQLERRM(100));
RAISE; --重新抛出异常
END;
--代码12.18 从异常中恢复的示例(这个才符合现实)
DECLARE
e_duplicate_name EXCEPTION; --定义异常
v_ename emp.ename%TYPE; --保存姓名的变量
v_newname emp.ename%TYPE := '李大牛'; --新插入的员工名称
BEGIN
BEGIN --在嵌套块中处理异常
SELECT ename INTO v_ename FROM emp WHERE empno = 7369;
IF v_ename = v_newname
THEN
RAISE e_duplicate_name; --如果产生异常,触发e_duplicate_name异常
END IF;
EXCEPTION
WHEN e_duplicate_name THEN
v_newname:='刘大夏';
END;
--如果没有异常,则执行插入语句
INSERT INTO emp VALUES (7882, v_newname, '职员', NULL, TRUNC (SYSDATE), 2000, 200, 20);
EXCEPTION --异常处理语句块
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('异常编码:'||SQLCODE||' 异常信息:'||SQLERRM);
END;
select * from emp for update;
--代码12.19 使用计数器获取异常信息(局限性:只能跟踪一个位置)
DECLARE
v_empno1 NUMBER(4):=&empno1; --定义员工查询条件变量
v_empno2 NUMBER(4):=&empno2;
v_empno3 NUMBER(4):=&empno3;
v_sal1 NUMBER(10,2); --定义保存员工薪资的变量
v_sal2 NUMBER(10,2);
v_sal3 NUMBER(10,2);
v_selectcounter NUMBER := 1; --查询计数器变量
BEGIN
SELECT sal INTO v_sal1 FROM emp WHERE empno=v_empno1; --查询员工薪资信息
v_selectcounter:=2;
SELECT sal INTO v_sal2 FROM emp WHERE empno=v_empno2;
v_selectcounter:=3;
SELECT sal INTO v_sal3 FROM emp WHERE empno=v_empno3;
EXCEPTION
WHEN NO_DATA_FOUND THEN --处理未找到数据的异常
DBMS_OUTPUT.PUT_LINE('错误编号:'||SQLCODE||' 错误消息:'||SQLERRM
||' 触发异常的位置是:'||v_selectcounter);
END;
--代码12.20 使用子块获取异常位置(上例的增强版)
DECLARE
v_empno1 NUMBER(4):=&empno1; --定义员工查询条件变量
v_empno2 NUMBER(4):=&empno2;
v_empno3 NUMBER(4):=&empno3;
v_sal1 NUMBER(10,2); --定义保存员工薪资的变量
v_sal2 NUMBER(10,2);
v_sal3 NUMBER(10,2);
BEGIN
BEGIN
SELECT sal INTO v_sal1 FROM emp WHERE empno=v_empno1; --查询员工薪资信息
EXCEPTION
WHEN NO_DATA_FOUND THEN --处理未找到数据的异常
DBMS_OUTPUT.PUT_LINE('错误编号:'||SQLCODE||' 错误消息:'||SQLERRM
||' 触发异常的位置是 1');
END;
BEGIN
SELECT sal INTO v_sal2 FROM emp WHERE empno=v_empno2;
EXCEPTION
WHEN NO_DATA_FOUND THEN --处理未找到数据的异常
DBMS_OUTPUT.PUT_LINE('错误编号:'||SQLCODE||' 错误消息:'||SQLERRM
||' 触发异常的位置是 2');
END;
BEGIN
SELECT sal INTO v_sal3 FROM emp WHERE empno=v_empno3;
EXCEPTION
WHEN NO_DATA_FOUND THEN --处理未找到数据的异常
DBMS_OUTPUT.PUT_LINE('错误编号:'||SQLCODE||' 错误消息:'||SQLERRM
||' 触发异常的位置是 3');
END;
EXCEPTION
WHEN NO_DATA_FOUND THEN --处理未找到数据的异常
DBMS_OUTPUT.PUT_LINE('错误编号:'||SQLCODE||' 错误消息:'||SQLERRM);
END;
--代码12.21 使用Format_Error_Backtrace函数获取异常位置
DECLARE
v_empno1 NUMBER(4):=&empno1; --定义员工查询条件变量
v_empno2 NUMBER(4):=&empno2;
v_empno3 NUMBER(4):=&empno3;
v_sal1 NUMBER(10,2); --定义保存员工薪资的变量
v_sal2 NUMBER(10,2);
v_sal3 NUMBER(10,2);
v_str VARCHAR2(200);
BEGIN
SELECT sal INTO v_sal1 FROM emp WHERE empno=v_empno1; --查询员工薪资信息
SELECT sal INTO v_sal2 FROM emp WHERE empno=v_empno2;
SELECT sal INTO v_sal3 FROM emp WHERE empno=v_empno3;
EXCEPTION
WHEN NO_DATA_FOUND THEN --处理未找到数据的异常
DBMS_OUTPUT.PUT_LINE('错误编号:'||SQLCODE||' 错误消息:'||SQLERRM
||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
END;
SELECT * FROM emp WHERE empno=7881;
--代码12.22 在异常中重复执行事务执行代码(Loop不可或缺)
DECLARE
e_duplicate_name EXCEPTION; --定义异常
v_ename emp.ename%TYPE; --保存姓名的变量
v_newname emp.ename%TYPE := '史密斯'; --新插入的员工名称
BEGIN
LOOP --开始循环
BEGIN --将语句块嵌入到子块中
SAVEPOINT 开始事务; --定义一个保存点
SELECT ename INTO v_ename FROM emp WHERE empno = &empno; --开始语句块代码
IF v_ename = v_newname
THEN
RAISE e_duplicate_name; --如果产生重复,触发e_duplicate_name异常
END IF;
INSERT INTO emp VALUES (7884, v_newname, '职员', NULL, TRUNC (SYSDATE), 2000, 200, 20);
COMMIT; --提交事务
EXIT; --提交完成退出循环
EXCEPTION --异常处理语句块
WHEN e_duplicate_name THEN
ROLLBACK TO 开始事务; --回滚事务到检查点位置
v_newname:='刘大夏'; --为产生异常的新员工名重新赋值,重新开始循环执行
END;
END LOOP;
END;