As we all known,程序的錯誤通常分爲兩類:編譯錯誤和運行時錯誤。其中運行時錯誤被稱爲異常。PL/SQL語句塊中處理異常的部分即爲異常處理部分。在異常處理部分,能夠指定當特定異常發生時所採起的動做。數據庫
PL/SQL有兩種類型的異常:內置異常和用戶自定義異常。編程
其中,內置異常又分爲預約義異常和非預約義異常。oracle
1、內置異常app
首先試舉一例來拋磚引玉。函數
DECLARE v_ename varchar2(10); v_empno number(4) := &v_empno; BEGIN SELECT ename INTO v_ename FROM EMP WHERE empno = v_empno; DBMS_OUTPUT.PUT_LINE('Employee name is '||v_ename); END;
該語句塊經過輸入員工的編號來得出員工的姓名。當輸入的員工編號存在時,輸出員工姓名,當員工編號不存在時,會有運行錯誤。以下所示:spa
SQL> / Enter value for v_empno: 7788 Employee name is SCOTT -->> 輸入的員工編號存在,輸出員工姓名 PL/SQL procedure successfully completed. SQL> / Enter value for v_empno: 1234 DECLARE * ERROR at line 1: ORA-01403: no data found -->> 輸入的員工編號不存在,報運行時錯誤 ORA-06512: at line 5
因而可知,編譯器沒法檢測運行錯誤。爲在程序中處理這種類型的錯誤,必須添加異常處理部分。異常處理部分的語法結構以下:指針
EXCEPTION
WHEN EXCEPTION_NAME THEN
ERROR-PROCESSING STATEMENTS;code
在語句塊中,異常處理部分位於可執行部分以後,上例可修改以下:對象
DECLARE v_ename varchar2(10); v_empno number(4) := &v_empno; BEGIN SELECT ename INTO v_ename FROM EMP WHERE empno = v_empno; DBMS_OUTPUT.PUT_LINE('Employee name is '||v_ename); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('There is no such employee'); END;
使用異常處理部分,可使得程序可以正常結束,而不是非正常終止。同時,輸出結果更加面向用戶,而不是編程人員。blog
上述NO_DATA_FOUND即爲Oracle預約義異常。
下面列出一些常見的預約義異常:
TOO_MANY_ROWS : SELECT INTO返回多行
INVALID_CURSOR :非法指針操做(關閉已經關閉的遊標)
ZERO_DIVIDE :除數等於零
DUP_VAL_ON_INDEX :違反惟一性約束
ACCESS_INTO_NULL: 未定義對象
CASE_NOT_FOUND: CASE 中若未包含相應的 WHEN ,而且沒有設置 ELSE 時
COLLECTION_IS_NULL: 集合元素未初始化
CURSER_ALREADY_OPEN: 遊標已經打開
DUP_VAL_ON_INDEX: 惟一索引對應的列上有重複的值
INVALID_NUMBER: 內嵌的 SQL 語句不能將字符轉換爲數字
NO_DATA_FOUND: 使用 select into 未返回行,或應用索引表未初始化的元素時
SUBSCRIPT_BEYOND_COUNT:元素下標超過嵌套表或 VARRAY 的最大值
SUBSCRIPT_OUTSIDE_LIMIT: 使用嵌套表或 VARRAY 時,將下標指定爲負數
VALUE_ERROR: 賦值時,變量長度不足以容納實際數據
LOGIN_DENIED: PL/SQL 應用程序鏈接到 oracle 數據庫時,提供了不正確的用戶名或密碼
NOT_LOGGED_ON: PL/SQL 應用程序在沒有鏈接 oralce 數據庫的狀況下訪問數據
PROGRAM_ERROR: PL/SQL 內部問題,可能須要重裝數據字典& pl./SQL 系統包
ROWTYPE_MISMATCH: 宿主遊標變量與 PL/SQL 遊標變量的返回類型不兼容
SELF_IS_NULL: 使用對象類型時,在 null 對象上調用對象方法
STORAGE_ERROR: 運行 PL/SQL 時,超出內存空間
SYS_INVALID_ID: 無效的 ROWID 字符串
TIMEOUT_ON_RESOURCE: Oracle 在等待資源時超時
others能夠表明全部異常,oracle預約義的異常在20000之內。
2、 用戶自定義異常
一般,在本身的程序裏,也許須要處理與所寫程序相關的問題。例如,在上個語句塊中,須要輸入員工編號。一般,但願員工編號是正值。可是無心間,用戶輸入一個負數。可是,沒有發生任何錯誤,由於變量v_empno被定義爲數值類型。這時,你但願自定義異常來處理這種狀況,這種類型的異常被稱爲用戶自定義異常。在使用該異常以前,必須首先進行聲明。語法結構以下所示:
DECLARE
exception_name EXCEPTION;
BEGIN
...
IF CONDITION THEN
RAISE exception_name;
ELSE
...
END IF;
EXCEPTION
WHEN exception_name THEN
ERROR-PROCESSING STATEMENTS;
END;
故上例可修改成:
DECLARE v_ename varchar2(10); v_empno number(4) := &v_empno; e_invalid_no exception; BEGIN IF v_empno < 0 THEN RAISE e_invalid_no; -->> 注意:RAISE語句應該與IF語句一塊兒使用,不然,每次執行時,執行權都會轉到該語句塊的異常處理部分 ELSE SELECT ename INTO v_ename FROM EMP WHERE empno = v_empno; DBMS_OUTPUT.PUT_LINE('Employee name is '||v_ename); END IF; EXCEPTION WHEN e_invalid_no THEN DBMS_OUTPUT.PUT_LINE('Employee number can not be negative'); END;
3、RAISE_APPLICATION_ERROR
RAISE_APPLICATION_ERROR是oracle提供的一種特殊的內置過程,容許編程人員爲特定應用程序建立有意義的錯誤信息。RAISE_APPLICATION_ERROR過程適用於未命名的用戶定義異常。它負責將錯誤編號和錯誤文本關聯起來,它的語法爲:
RAISE_APPLICATION_ERROR(error_number,error_message);
error_number是與特定錯誤信息相關聯的錯誤編號。這個編號的範圍在-20999到-20000之間。error_message是錯誤文本,最多包含2048個字符。
上例可修改成:
DECLARE v_ename varchar2(10); v_empno number(4) := &v_empno; BEGIN IF v_empno < 0 THEN RAISE_APPLICATION_ERROR(-20000,'Employee number can not be negative'); ELSE SELECT ename INTO v_ename FROM EMP WHERE empno = v_empno; DBMS_OUTPUT.PUT_LINE('Employee name is '||v_ename); END IF; END;
當輸入的員工編號爲負數時,運行結果以下所示:
SQL> / Enter value for v_empno: -1234 DECLARE * ERROR at line 1: ORA-20000: Employee number can not be negative ORA-06512: at line 6
藉助於RAISE_APPLICATION_ERROR過程,編程人員可以遵循與Oracle錯誤一致的方式返回錯誤信息。
4、 EXCEPTION_INIT
在上文內置異常中,預約義異常的個數實際上是很是有限的,當程序拋出其它不在上述預約義範圍內的異常時,該如何捕捉呢?
譬以下例:
DECLARE v_deptno number(2) := &v_deptno; BEGIN DELETE FROM dept WHERE deptno= v_deptno; END;
當部門編號輸入10時,咱們來看看運行結果:
SQL> / Enter value for v_deptno: 10 DECLARE * ERROR at line 1: ORA-02292: integrity constraint (SCOTT.FK_DEPTNO) violated - child record found ORA-06512: at line 4
違反父鍵約束,可是,咱們如何捕捉此種錯誤呢?在這裏,咱們能夠用到EXCEPTION_INIT。
使用EXCEPTION_INIT指令,能夠將某Oracle錯誤編號和用戶定義異常的名稱創建關聯。EXCEPTION_INIT指令出如今語句塊的聲明部分,以下所示:
DECLARE
exception_name EXCEPTION;
PRAGMA EXCEPTION_INIT(exception_name,error_code);
注意,用戶定義異常的聲明出如今所使用的EXCEPTION_INIT指令以前,EXCEPTION_INIT指令有兩個參數:exception_name和error_code。exception_name是異常的名稱,error_code是但願與該異常創建關聯的Oralce錯誤編號。
上例可修改成:
DECLARE v_deptno number(2) := &v_deptno; e_child_exists EXCEPTION; PRAGMA EXCEPTION_INIT(e_child_exists,-2292); BEGIN DELETE FROM dept WHERE deptno= v_deptno; EXCEPTION WHEN e_child_exists THEN DBMS_OUTPUT.PUT_LINE('Delete employees for No.'||v_deptno||' dept first'); END;
一樣將部門編號輸入爲10,來看看結果:
SQL> / Enter value for v_deptno: 10 Delete employees for No. 10 dept frist PL/SQL procedure successfully completed.
能夠正常捕捉錯誤!
5、 SQLCODE和SQLERRM
因此Oracle錯誤均可以使用OTHERS異常處理程序進行捕獲和處理,以下例所示:
DECLARE v_deptno number(4) := &v_deptno; v_dname varchar2(5); v_loc varchar2(10); BEGIN SELECT dname,loc INTO v_dname,v_loc FROM dept WHERE deptno = v_deptno; DBMS_OUTPUT.PUT_LINE(v_dname||','||v_loc); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('An error has occurred'); END;
當輸入10做爲部門編號的值時,咱們來看看輸出:
SQL> / Enter value for v_deptno: 10 An error has occurred PL/SQL procedure successfully completed.
上述輸出說明,在程序運行時發生一個錯誤。若是你對錶結構及數據不是很熟悉的話,你很難知道這個錯誤是什麼,以及是什麼緣由致使錯誤發生的。也許在運行時,dept表中不存在對應的部門編號,或者SELECT INTO語句所致使的數據類型匹配問題。儘管這只是一個簡單地例子,但仍舊可能會發生不少意想不到的運行錯誤。
固然,你永遠沒法知道程序執行時全部可能發生的運行錯誤,所以,最好在本身的程序中添加OTHERS異常處理程序。爲改進本身程序的異常處理接口,Oracle提供了兩個內置函數-SQLCODE和SQLERRM-用於實現OTHERS異常處理程序。SQLCODE函數會返回Oracle錯誤編號,SQLERRM函數返回錯誤信息。
修改上例以下:
DECLARE v_deptno number(4) := &v_deptno; v_dname varchar2(5); v_loc varchar2(10); BEGIN SELECT dname,loc INTO v_dname,v_loc FROM dept WHERE deptno = v_deptno; DBMS_OUTPUT.PUT_LINE(v_dname||','||v_loc); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(SQLCODE||chr(10)||SQLERRM); -->> chr(10)表明回車鍵 END;
一樣,當輸入10做爲部門編號時,看看輸出:
SQL> / Enter value for v_deptno: 10 -6502 ORA-06502: PL/SQL: numeric or value error: character string buffer too small PL/SQL procedure successfully completed.
這樣可捕捉任何運行時錯誤的錯誤編號和錯誤信息
總結:
1> 預約義異常的錯誤代碼有名稱,譬如上文提到的NO_DATA_FOUNG
2> 非預約義異常只有錯誤代碼,沒有名稱,如上文提到的ora-02292。這時能夠經過EXCEPTION_INIT編譯指令進行錯誤代碼和名稱的關聯。
3> 當PL/SQL語句塊的可執行部分出現某個運行錯誤時,會拋出不一樣類型的異常。可是,運行錯誤也可能發生在語句塊的聲明部分或者異常處理部分。控制在這些環境下異常拋出方式的規則稱爲異常傳播。
4> 當PL/SQL語句塊的聲明部分或者異常處理部分出現運行錯誤時,該語句塊的異常處理部分不能捕獲此項錯誤。若是不存在外部語句塊,該程序執行會終止,並將執行權轉到主機環境。若是存在外部語句塊,該異常會當即傳播到外部語句塊。以下例所示:
--outer block BEGIN --inner block DECLARE v_test CHAR(3) := 'ABC'; BEGIN v_test := '1234'; DBMS_OUTPUT.PUT_LINE('v_test: '||v_test); EXCEPTION WHEN INVALID_NUMBER OR VALUE_ERROR THEN v_test := 'ABCD'; DBMS_OUTPUT.PUT_LINE('An error has occurred in the inner block'); END; EXCEPTION WHEN INVALID_NUMBER OR VALUE_ERROR THEN DBMS_OUTPUT.PUT_LINE('An error has occurred in the program'); END;
當執行時,獲得以下輸出:
SQL> / An error has occurred in the program PL/SQL procedure successfully completed.
最後試舉一例練練思惟:
DECLARE my_error1 EXCEPTION; PRAGMA EXCEPTION_INIT(my_error1, -20001); my_error2 EXCEPTION; PRAGMA EXCEPTION_INIT(my_error2, -20002); BEGIN IF 1=2 THEN raise_application_error(-20001,'err_1'); ELSE raise_application_error(-20002,'err_2'); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('Not found'); WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('More data'); WHEN MY_ERROR1 THEN dbms_output.put_line('This is a err_1 test'); WHEN MY_ERROR2 THEN dbms_output.put_line('This is a err_2 test'); END;