PL/SQL異常處理

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;
相關文章
相關標籤/搜索