PL SQL異常處理.

wzhalal發表於2013-11-26
PL/SQL程式執行過程中出現的錯誤,稱之為異常。


PL/SQL程式的錯誤可分為兩類:
    一類是PL/SQL語法錯誤,它由PL/SQL編譯器發現並給出錯誤資訊,
    另一類是執行時錯誤,由PL/SQL執行時引擎發現並給出錯誤資訊。


異常分類:
    PL/SQL語言的異常分為兩大類
一類是ORACLE系統異常
ORACLE系統異常又分為兩種,一種是預定義異常,另一種是非預定義異常。
    另一類是自定義異常


    (1)預定義異常
預定義異常是ORACLE系統異常中的一種,用於處理常見的ORACLE錯誤。ORACLE預定義異常的特點是,
ORACLE系統定義了它們的錯誤編號與異常名字。當執行PL/SQL程式碼產生預定義錯誤時,與錯誤對應的
預定義異常被自動丟擲,通過預定義異常名字捕獲該異常。
預定義異常與ORACLE錯誤編號之間的對應關係如下所示:

預定義異常   Oracle錯誤   錯誤程式碼  描述
  
ACCESS_INTO_NULL  ORA-06530   -6530 試圖給一個沒有初始化(為NULL)的物件賦值
 
CASE_NOT_FOUND    ORA-06592    -6592 在CASE語句中沒有WHEN子句被選擇,並且沒
  有ELSE子句


COLLECTION_IS_NULLORA-06531   -6531 試圖將EXISTS以外的集合(collection)方法
 應用到一個沒有被初始化(為NULL)的巢狀表
(nested table)和變長陣列(varray)中,
 或者試圖給沒有初始化的巢狀表
(nested table)或變長陣列()varray中的
 元素賦值


CURSOR_ALREADY_OPENORA-06511   -6511 試圖開啟一個已經開啟的遊標,一個遊標在它
 重新開啟前必須被關閉。一個遊標FOR迴圈會
 自動地開啟所涉及的遊標,所以在遊標迴圈裡
 不能開啟遊標


DUL_VAL_ON_INDEX  ORA-00001   -1   試圖在一個有惟一性約束的資料庫列中儲存重複的值


  INVALID_CURSOR  ORA-01001   -1001 試圖執行一個無效的遊標操作  


INVALID_NUMBER  ORA-01722   -1722 試圖將一個看起來不像是一個有效的數字的字
 符串轉換成數字失敗時,而在過程性語句中,
 將會引發VALUE_ERROR錯誤,代替
 INVALID_NUMBER錯誤


LOGIN_DENIED  ORA-01017   -1017 用一個無效的使用者名稱或口令去登陸Oracle


NO_DATA_FOUND  ORA-01403   100 一個SELECT INTO語句沒有返回資料或者程式
 引用一個巢狀表中被刪除的元素或索引表中一
 個沒有被初始化的元素  

NOT_LOGGED_ON  ORA-01012   -1012 釋出一個資料庫呼叫指令而沒有連線到資料庫 


PROGRAM_ERROR  ORA-06501   -6501 Oracle內在錯誤,通常是由PL/SQL本身造成
 的,這種情況下應該通知Oracle公司技術部門 


ROWTYPE_MISMATCH  ORA-06504   -6504 宿主遊標變數和PL/SQL遊標變數相關的作業有
 著不相容的型別 


SELF_IF_NULL  ORA-30625   -30625 程式試圖呼叫一個空的例項的成員方法


STORAGE_ERROR ORA-06500 -6500 PL/SQL執行時記憶體不夠或者記憶體是有問題的 

SUBSCRIPT_BEYOND_COUNT  ORA-06533  -6533 引用一個巢狀表(nested table)或變長數
 組(varray)的元素時索引值大於集合中元素
 的數量 


SUBSCRIPT_OUTSIDE_LIMIT  ORA-06532 -6532 引用一個巢狀表(nested table)或變長數
 組(varray)的元素時使用的索引值(例如
 -1)超出了規定的範圍 


SYS_INVALID_ROWID  ORA-01410   -1410 試圖將一個看起來不像是一個有效的ROWID的字
 符串轉換成一個ROWID時  


TIMEOUT_ON_RESOURCE  ORA-00051  -51  Oracle在等待資源時發生超時現象  


TOO_MANY_ROWS ORA-01422 -1422 SELECT INTO語句返回了多行資料 


VALUE_ERROR   ORA-06502   -6502 一個演算法、轉換、截斷或者大小約束錯誤發生,
 如果在SQL語句中發生錯誤則會引發
INVALID_ERROR錯誤,替代了VALUE_ERROR錯誤 


  ZERO_DIVIDE  ORA-01476   -1476 發生被零除 


    (2)非預定義異常
也是ORACLE系統異常中的一種,用於處理預定義以外的ORACLE系統錯誤。ORACLE非預定義異常的特點
是,ORACLE系統定義了它們的錯誤編號,但沒有定義異常名字。這些ORACLE系統錯誤沒有預定義異
常(異常名字)與其關聯,需要在PL/SQL塊的宣告部分定義一個異常名字,然後通過偽過程
PRAGMA EXCEPTION_INIT 將該異常名字與一個ORACLE 錯誤編號相關聯。這樣,當執行PL/SQL程式碼
產生非預定義錯誤時,與錯誤程式碼對應的非預定義異常被自動丟擲,通過定義的異常名字捕獲該異常,
並對錯誤進行處理。


    (3)自定義異常
自定義異常用於處理使用者定義錯誤,即處理與ORACLE系統錯誤無關的其錯誤。自定義異常是指有些操
作不會產生ORACLE系統錯誤,但是程式設計師從業務規則角度考慮,認為是一種錯誤。例如,執行UPDATE
操作沒有更新任何記錄行時,不會引發ORACLE系統錯誤,也不會發生異常。但是,有時需要開發人員
為此操作產生一個異常,以便進行處理,這就是使用者定義異常。


異常處理
PL/SQL程式中,異常處理按以下3個步驟進行:
定義異常
丟擲異常
捕獲及處理異常


(1)定義異常
如前所述,ORACLE中的異常分為預定義異常、非預定義異常和自定義異常,其中預定義異常
由系統隱式定義,而後兩種異常則需要使用者定義。定義異常的方法是在PL/SQL程式的宣告
部分定義一個EXCEPTION型別的變數,其格式為:


exception_name EXCEPTION; 


其中exception_name 為異常名,如果 是非預定義異常,還需要使用偽過程,在編譯階段將
異常名與一個ORACLE錯誤程式碼相關聯,其語句格式為:

PRAGMA EXCEPTION_INIT(exception_name,error_number);


其中,exception_name為異常名,error_number為ORACLE系統內部錯誤號,用一個負位數
表示,-20999~-20000為使用者定義錯誤的保留號。


(2)丟擲異常
由於系統不能自動識別使用者定義錯誤,因此當產生自定義錯誤時,需要程式設計師使用特定的PL/SQL
程式碼丟擲相應的自定義異常。自定義異常的丟擲語句格式為:


RAISE exception_name;

(3)捕獲及處理異常
異常處理的基本格式為:

EXCEPTION
WHEN e_name1 [OR e_name2...] THEN
sequence of statements1;
WHEN e_name3 [OR e_name4...] THEN
sequence of statements2;
......
[WHEN OTHERS THEN
sequence of statementsn;]
END;


異常處理例項
處理預定義異常


   例1:
DECLARE
v_dividend NUMBER:=50;
v_divisor NUMBER:=0;
v_quotient NUMBER;
BEGIN
v_quotient:=v_dividend/v_divisor;
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('divisor is 0');
END;
/


   例2:輸入員工編號,如果員工不存在,則提示錯誤
SQL> SET SERVEROUTPUT ON


DECLARE
v_id emp.empno%TYPE;
v_name emp.ename%TYPE;
BEGIN
v_id:=&inputid;
select ename INTO v_name FROM emp WHERE empno=v_id;
DBMS_OUTPUT.PUT_LINE('EMPNO = '||v_name);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('NOT FOUND RECORD!');
END;
/


   例3:使用兩種異常處理,避免找不到記錄和找到多條記錄而賦給一個變數的錯誤


DECLARE
v_job emp.job%TYPE;
v_name emp.ename%TYPE;
BEGIN
v_job:='&inputjob';
select ename INTO v_name FROM emp WHERE job=v_job;
DBMS_OUTPUT.PUT_LINE('EMPNO = '||v_name);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('NOT FOUND RECORD!');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('FOUND RECORD TOO MANY!');
END;
/


非預定義異常
也是ORACLE系統異常中的一種,用於處理預定義以外的ORACLE系統錯誤。ORACLE非預定義異常的特點
是,ORACLE系統定義了它們的錯誤編號,但沒有定義異常名字。這些ORACLE系統錯誤沒有預定義異
常(異常名字)與其關聯,需要在PL/SQL塊的宣告部分定義一個異常名字,然後通過偽過程
PRAGMA EXCEPTION_INIT 將該異常名字與一個ORACLE 錯誤編號相關聯。這樣,當執行PL/SQL程式碼
產生非預定義錯誤時,與錯誤程式碼對應的非預定義異常被自動丟擲,通過定義的異常名字捕獲該異常,
並對錯誤進行處理。
非預定義異常需要使用者定義異常名,而且還要使用偽過程,在編譯階段將異常名與一個ORACLE
錯誤程式碼相關聯。


   例1:在dept刪除10號部門,試驗引用完整性約束異常
SQL>    DECLARE
  2                     e_deptid EXCEPTION;
  3                     PRAGMA EXCEPTION_INIT(e_deptid,-2292);
  4             BEGIN
  5                     DELETE FROM DEPT WHERE DEPTNO=&e_deptid ;
  6             EXCEPTION
  7                     WHEN e_deptid THEN
  8                             DBMS_OUTPUT.PUT_LINE('at sub table have record!');
  9             END;
 10             /
輸入 e_deptid 的值:  20
原值    5:                      DELETE FROM DEPT WHERE DEPTNO=&e_deptid ;
新值    5:                      DELETE FROM DEPT WHERE DEPTNO=20 ;
at sub table have records !


PL/SQL 過程已成功完成。


SQL>    DECLARE
  2                     e_deptid EXCEPTION;
  3                     PRAGMA EXCEPTION_INIT(e_deptid,-2292);
  4             BEGIN
  5                     DELETE FROM DEPT WHERE DEPTNO=&e_deptid ;
  6             EXCEPTION
  7                     WHEN e_deptid THEN
  8                             DBMS_OUTPUT.PUT_LINE('at sub table have record!');
  9             END;
 10             /
輸入 e_deptid 的值:  40
原值    5:                      DELETE FROM DEPT WHERE DEPTNO=&e_deptid ;
新值    5:                      DELETE FROM DEPT WHERE DEPTNO=40 ;


PL/SQL 過程已成功完成。


   例2:在emp表中插入記錄,部門號為80,試驗參照完整性約束異常


DECLARE
e_deptid EXCEPTION;
PRAGMA EXCEPTION_INIT(e_deptid,-2291);
BEGIN
INSERT INTO EMP (EMPNO,DEPTNO) VALUES (8001,80); 
EXCEPTION
WHEN e_deptid THEN
DBMS_OUTPUT.PUT_LINE('deptno 80 at table dept no exist !');
END;
/


deptno 80 at table dept no exist !


PL/SQL 過程已成功完成。


   例3:在emp表中插入一條EMPNO存在的記錄


DECLARE
e_deptid EXCEPTION;
PRAGMA EXCEPTION_INIT(e_deptid,-0001);
BEGIN
INSERT INTO EMP (EMPNO,DEPTNO) VALUES (7788,10); 
EXCEPTION
WHEN e_deptid THEN
DBMS_OUTPUT.PUT_LINE('record is exist !');
END;
/


SQL> select empno,deptno from emp where empno=7788;


     EMPNO     DEPTNO
---------- ----------
      7788         10


自定義異常處理
自定義異常不僅需要使用者定義異常名字,還要程式設計師安排何時丟擲異常及對異常的處理。


   例1:在emp中插入一條工資為負數的記錄。


DECLARE
e_sal EXCEPTION;
v_sal emp.sal%TYPE;
BEGIN
v_sal:=&inputsal;
IF v_sal<0 THEN
RAISE e_sal;
END IF;
INSERT INTO emp(empno,ename,sal) values (8002,'TEST',v_sal);
EXCEPTION
WHEN e_sal THEN
DBMS_OUTPUT.PUT_LINE('sal not < 0 !');
END;
/


輸入 inputsal 的值:  -1000
原值    5:                      v_sal:=&inputsal;
新值    5:                      v_sal:=-1000;
sal not < 0 !


PL/SQL 過程已成功完成。


   例2:帶有WHEN OTHERS THEN的異常處理


DECLARE
e_sal EXCEPTION;
v_sal emp.sal%TYPE;
BEGIN
v_sal:=&inputsal;
INSERT INTO emp(empno,ename,sal) values (8002,'TEST',v_sal);
IF v_sal<0 THEN
RAISE e_sal;
END IF;
EXCEPTION
WHEN e_sal THEN
DBMS_OUTPUT.PUT_LINE('sal not < 0 !');
rollback;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('other error !');
rollback;
END;
/


使用異常函式
在PL/SQL程式碼執行出現錯誤時,通過使用異常函式可以獲得錯誤程式碼及相關的錯誤描述,
其中函式SQLCODE用於獲得ORACLE錯誤程式碼,而SQLERRM則用於獲得與之相應 的錯誤描述。


DECLARE
e_sal EXCEPTION;
v_sal emp.sal%TYPE;
v_empno emp.empno%TYPE;
v_deptno emp.deptno%TYPE;
BEGIN
v_sal:=&inputsal;
v_empno:=&inputempno;
v_deptno:=&inputdeptno;
INSERT INTO emp(empno,deptno,sal) values (v_empno,v_deptno,v_sal);
IF v_sal<0 THEN
RAISE e_sal;
END IF;
EXCEPTION
WHEN e_sal THEN
DBMS_OUTPUT.PUT_LINE('sal not < 0 !');
rollback;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error code:'||SQLCODE);
DBMS_OUTPUT.PUT_LINE('error desc:'||SQLERRM);
END;
/


第一種情況:
輸入 inputsal 的值:     100
原值    7:              v_sal:=&inputsal;
新值    7:              v_sal:= 100;
輸入 inputempno 的值:  100
原值    8:              v_empno:=&inputempno;
新值    8:              v_empno:=100;
輸入 inputdeptno 的值:  10
原值    9:              v_deptno:=&inputdeptno;
新值    9:              v_deptno:=10;


PL/SQL 過程已成功完成。


第二種情況:
輸入 inputsal 的值:     -1000
原值    7:              v_sal:=&inputsal;
新值    7:              v_sal:= -1000;
輸入 inputempno 的值:  1000
原值    8:              v_empno:=&inputempno;
新值    8:              v_empno:=1000;
輸入 inputdeptno 的值:  20
原值    9:              v_deptno:=&inputdeptno;
新值    9:              v_deptno:=20;
sal not < 0 !


PL/SQL 過程已成功完成。


























來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28894640/viewspace-1061167/,如需轉載,請註明出處,否則將追究法律責任。