源码-PL/SQL从入门到精通-第十二章-错误处理机制-Part 2

源码-PL/SQL从入门到精通-第十二章-异常处理机制-Part 2

异常处理可以让程序更“健壮”,品质更高,所以很重要!

好在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;

相关内容推荐