PL/SQL編程基礎(五):異常處理(EXCEPTION)


異常處理

  • 異常產生所帶來的問題;
  • 使用EXCEPTION程序塊進行異常處理;
  • 實現用戶自定義異常。
  • 使用異常可以保證在程序中出現運行時異常時程序可以正常的執行完畢;
  • 用戶可以使用自定義異常進行操作。

 

異常簡介

  • 在程序開發之中經常會由於設計錯誤、編碼錯誤、硬件故障或其他原因引起程序的運行錯誤。雖然不可能預測所有錯誤,但在程序中可以規划處理某些類型的錯誤。在PL/SQL程序中的異常處理機制使得在出現某些錯誤的時候程序仍然可以執行。比如內部溢出或者零除等等。
  • 用戶可以處理的只有運行時異常,而對於編譯的異常,只能通過語法解決。
  • 異常情況處理(EXCEPTION)是用來處理正常執行過程中未預料的事件,程序塊的異常處理預定義的錯誤和自定義錯誤,由於PL/SQL程序塊一旦產生異常而沒有指出如何處理時,程序就會自動終止整個程序運行.
  • 有三種類型的異常錯誤:

    1. 預定義 ( Predefined )錯誤

 ORACLE預定義的異常情況大約有24個。對這種異常情況的處理,無需在程序中定義,由ORACLE自動將其引發。

    2. 非預定義 ( Predefined )錯誤

即其他標准的ORACLE錯誤。對這種異常情況的處理,需要用戶在程序中定義,然后由ORACLE自動將其引發。

    3. 用戶定義(User_define) 錯誤

 程序執行過程中,出現編程人員認為的非正常情況。對這種異常情況的處理,需要用戶在程序中定義,然后顯式地在程序中將其引發。

  • 異常處理部分一般放在 PL/SQL 程序體的后半部,結構為:

EXCEPTION

   WHEN first_exception THEN  <code to handle first exception >

   WHEN second_exception THEN  <code to handle second exception >

   WHEN OTHERS THEN  <code to handle others exception >

END;

異常處理可以按任意次序排列,但 OTHERS 必須放在最后.

預定義說明的部分 ORACLE 異常錯誤

錯誤號

異常錯誤信息名稱

說明

ORA-0001

Dup_val_on_index

違反了唯一性限制

ORA-0051

Timeout-on-resource

在等待資源時發生超時

ORA-0061

Transaction-backed-out

由於發生死鎖事務被撤消

ORA-1001

Invalid-CURSOR

試圖使用一個無效的游標

ORA-1012

Not-logged-on

沒有連接到ORACLE

ORA-1017

Login-denied

無效的用戶名/口令

ORA-1403

No_data_found

SELECT INTO沒有找到數據

ORA-1422

Too_many_rows

SELECT INTO 返回多行

ORA-1476

Zero-divide

試圖被零除

ORA-1722

Invalid-NUMBER

轉換一個數字失敗

ORA-6500

Storage-error

內存不夠引發的內部錯誤

ORA-6501

Program-error

內部錯誤

ORA-6502

Value-error

轉換或截斷錯誤

ORA-6504

Rowtype-mismatch

宿主游標變量與 PL/SQL變量有不兼容行類型

ORA-6511

CURSOR-already-OPEN

試圖打開一個已處於打開狀態的游標

ORA-6530

Access-INTO-null

試圖為null 對象的屬性賦值

ORA-6531

Collection-is-null

試圖將Exists 以外的集合( collection)方法應用於一個null pl/sql 表上或varray上

ORA-6532

Subscript-outside-limit

對嵌套或varray索引得引用超出聲明范圍以外

ORA-6533

Subscript-beyond-count

對嵌套或varray 索引得引用大於集合中元素的個數.

對這種異常情況的處理,只需在PL/SQL塊的異常處理部分,直接引用相應的異常情況名,並對其完成相應的異常錯誤處理即可。

 

非預定義的異常處理

 對於這類異常情況的處理,首先必須對非定義的ORACLE錯誤進行定義。步驟如下:

1. 在PL/SQL 塊的定義部分定義異常情況:

<異常情況>  EXCEPTION;

2. 將其定義好的異常情況,與標准的ORACLE錯誤聯系起來,使用EXCEPTION_INIT語句:

PRAGMA EXCEPTION_INIT(<異常情況>, <錯誤代碼>);

3. 在PL/SQL 塊的異常情況處理部分對異常情況做出相應的處理。

 

 

用戶自定義的異常處理

當與一個異常錯誤相關的錯誤出現時,就會隱含觸發該異常錯誤。用戶定義的異常錯誤是通過顯式使用 RAISE 語句來觸發。當引發一個異常錯誤時,控制就轉向到 EXCEPTION塊異常錯誤部分,執行錯誤處理代碼。 

對於這類異常情況的處理,步驟如下:

1. 在PL/SQL 塊的定義部分定義異常情況:

<異常情況>  EXCEPTION;

2. RAISE <異常情況>;

 

3. 在PL/SQL 塊的異常情況處理部分對異常情況做出相應的處理。

 

用戶定義的異常處理

  調用DBMS_STANDARD(ORACLE提供的包)包所定義的RAISE_APPLICATION_ERROR過程,可以重新定義異常錯誤消息,它為應用程序提供了一種與ORACLE交互的方法。

RAISE_APPLICATION_ERROR 的語法如下:

 RAISE_APPLICATION_ERROR(error_number,error_message,[keep_errors] );

   error_number

–20,000 到 –20,999 之間的參數,

    error_message

是相應的提示信息(< 2048 字節),

 keep_errors

可選,如果keep_errors =TRUE ,則新錯誤將被添加到已經引發的錯誤列表中。如果keep_errors=FALSE(缺省),則新錯誤將替換當前的錯誤列表。

 

 

  • PL/SQL程序之中一共分為兩種異常類型:

編譯型異常:

程序的語法出現了錯誤所導致的異常;

運行時異常:

程序沒有語法問題,但在運行時會因為程序運算或者返回結果而出現錯誤。

編譯型異常

程序由於語法出現問題,無法正常編譯通過.

DECLARE

result NUMBER := 1;

BEGIN

IF result = 1    -- 此處語法有錯誤,缺少THEN

DBMS_OUTPUT.put_line('條件滿足。') ;

END IF ;

END ;

/

運行時異常

ERROR at line 1:

ORA-01476: divisor is equal to zero

ORA-06512: at line 4

DECLARE

varA    NUMBER ;

BEGIN

varA := 10 / 0 ;        -- 被除數為0

END ;

/

 

異常處理

  • 如果要進行異常處理,需要使用EXCEPTION子句完成,這個子句里面要針對異常進行捕獲,而后針對捕獲的異常進行處理.

 

使用EXCEPTION來處理異常

  • PL/SQL的基本結構之中,給出了EXCEPTION語句塊來讓用戶編寫異常處理的代碼,而在進行異常處理之前,首先還需要判斷出現的是何種異常,所以在EXCEPTION語句塊中處理格式如下。
    • WHEN 異常類型 | 用戶定義異常 | 異常代碼 | OTHERS THEN

異常處理 ;

處理被除數為零異常

DECLARE

varA    NUMBER ;

BEGIN

varA := 10 / 0 ;        -- 被除數為0

DBMS_OUTPUT.put_line('異常之后的代碼將不再執行!') ;

EXCEPTION

WHEN zero_divide THEN

DBMS_OUTPUT.put_line('被除數不能為零。') ;

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

END ;

/

處理賦值異常

DECLARE

varA    VARCHAR2(1) ;

varB    VARCHAR2(4) := 'java' ;

BEGIN

varA := varB ;        -- 錯誤的賦值

DBMS_OUTPUT.put_line('異常之后的代碼將不再執行!') ;

EXCEPTION

WHEN value_error THEN

DBMS_OUTPUT.put_line('數據賦值錯誤。') ;

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

END ;

/

處理SQL異常 —— 找不到數據

DECLARE

eno        emp.empno%TYPE ;

ename    emp.ename%TYPE ;

BEGIN

eno := &empno ;        -- 由鍵盤輸入雇員編號

SELECT ename INTO ename FROM emp WHERE empno=eno ;

DBMS_OUTPUT.put_line('編號為:' || eno || '雇員的名字為:'|| ename) ;

EXCEPTION

WHEN no_data_found THEN

DBMS_OUTPUT.put_line('沒有這個雇員!') ;

END ;

/

處理SQL異常 —— 返回多條結果

一個變量只能接收一個內容,如果返回的數值是多行數據,就處理不了

DECLARE

dno        emp.deptno%TYPE ;

ename    emp.ename%TYPE ;

BEGIN

dno := &deptno ;        -- 由鍵盤輸入部門編號

SELECT ename INTO ename FROM emp WHERE deptno=dno ;

EXCEPTION

WHEN too_many_rows THEN

DBMS_OUTPUT.put_line('返回的數據過多!') ;

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

END ;

/

使用others來捕獲所有異常

DECLARE

result    NUMBER ;

title    VARCHAR2(50) := 'SCENT' ;

BEGIN

result := title ;    -- 此處出現異常

EXCEPTION

WHEN others THEN

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

DBMS_OUTPUT.put_line('SQLERRM = ' || SQLERRM) ;

END ;

/

 

異常處理流程

 


 

用戶自定義異常

  • 除了以上由Oracle自己定義的異常之外,用戶在程序編寫中,也可以使用兩種方式來定義用戶異常:

方式一

在聲明塊中聲明EXCEPTION對象,此方式有兩種選擇:

    • 選擇一:聲明異常對象並用名稱來引用它,此方式使用普通的others異常捕獲用戶定義異常;
    • 選擇二:聲明異常對象並將它與有效的Oracle錯誤代碼映射,需要編寫單獨的WHEN語句塊捕獲;

方式二

在執行塊中構建動態異常。通過“RAISE_APPLICATION_ERROR”函數可以構建動態異常。在觸發動態異常時,可使用-20000 ~ -20999范圍的數字。如果使用動態異常,可以在運行時指派錯誤消息。

使用用戶定義異常

DECLARE

data        NUMBER ;

myexp    EXCEPTION ;   --定義了一個異常變量

BEGIN

data := &inputData ;    --輸入數據

IF data > 10 AND data < 100 THEN

RAISE myexp ; --拋出異常

END IF ;

EXCEPTION

WHEN myexp THEN --出現指定的異常

DBMS_output.put_line('輸入數據有錯誤!') ;

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

DBMS_OUTPUT.put_line('SQLERRM = ' || SQLERRM) ;

END;

/

DECLARE

data        NUMBER ;

myexp    EXCEPTION ;   --定義了一個異常變量

BEGIN

data := &inputData ;    --輸入數據

IF data > 10 AND data < 100 THEN

RAISE myexp ; --拋出異常

END IF ;

EXCEPTION

WHEN others THEN --出現指定的異常

DBMS_output.put_line('輸入數據有錯誤!') ;

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

DBMS_OUTPUT.put_line('SQLERRM = ' || SQLERRM) ;

END;

/

將系統異常轉為用戶定義異常

用戶自己設置了一個錯誤編碼,也可以和一個存在的編碼進行連接.

DECLARE

data            NUMBER ;

input_exception        EXCEPTION ; --定義一個異常變量

PRAGMA            EXCEPTION_INIT(input_exception,-20789) ;

BEGIN

data := &inputData ;

IF data > 10 AND data < 100 THEN

RAISE input_exception ;

END IF ;

EXCEPTION

WHEN input_exception THEN

DBMS_output.put_line('輸入數據有錯誤!') ;

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

DBMS_OUTPUT.put_line('SQLERRM = ' || SQLERRM) ;

END;

/

綁定已有的錯誤號

DECLARE

myexp        EXCEPTION ;

v_input_rowid    VARCHAR2(18) ;

PRAGMA EXCEPTION_INIT(myexp,-1410) ;

BEGIN

v_input_rowid := '&inputRowid' ;

IF LENGTH(v_input_rowid)<>18 THEN    -- 長度不足18

RAISE myexp ;

END IF ;

EXCEPTION

WHEN myexp THEN

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

DBMS_OUTPUT.put_line('SQLERRM = ' || SQLERRM) ;

END ;

/

 

 

構建動態異常

  • 程序之中,也可以將用戶定義的異常添加到異常列表(異常堆棧)之中,其語法如下所示。
    • RAISE_APPLICATION_ERROR(錯誤號,錯誤信息 [,是否添加到錯誤堆棧])
    • 語法參數:
      • 錯誤號:

        只接受-20000 ~ -20999范圍的錯誤號,和聲明的錯誤號一致;

        錯誤信息:

        用於定義在使用SQLERRM輸出時的錯誤提示信息;

        是否添加到錯誤堆棧:

        如果設置為TRUE,則表示將錯誤添加到任意已有的錯誤堆棧,默認為FALSE,可選。

設置錯誤號

設置錯誤信息

EXCEPTION中使用指定異常對象.

直接使用others,所有的異常都交給other一起處理了,不能分割,如果異常要求嚴格,還是需要分割的。

 

構建動態異常

DECLARE

data        NUMBER ;

myexp    EXCEPTION ;

PRAGMA    EXCEPTION_INIT(myexp,-20789) ; --定義一個異常變量

BEGIN

DBMS_OUTPUT.put_line('請輸入數字:') ;

data := &inputData ;

IF data > 10 AND data < 100 THEN

RAISE_APPLICATION_ERROR(-20789,'輸入數字不能在10~100之間!') ; --拋出異常

END IF ;

EXCEPTION

WHEN myexp THEN

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

DBMS_OUTPUT.put_line('SQLERRM = ' || SQLERRM) ;

END;

/

使用others

DECLARE

data        NUMBER ;

myexp    EXCEPTION ;

PRAGMA    EXCEPTION_INIT(myexp,-20789) ; --定義一個異常變量

BEGIN

DBMS_OUTPUT.put_line('請輸入數字:') ;

data := &inputData ;

IF data > 10 AND data < 100 THEN

RAISE_APPLICATION_ERROR(-20789,'輸入數字不能在10~100之間!') ; --拋出異常

END IF ;

EXCEPTION

WHEN others THEN

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

DBMS_OUTPUT.put_line('SQLERRM = ' || SQLERRM) ;

END;

/

范例,插入部門信息.

DECLARE

v_dno   dept.deptno%TYPE;--部門編號

v_dna   dept.dname%TYPE;--部門名稱

v_dloc   dept.loc%TYPE;--部門位置

v_deptCount   NUMBER;--保存COUNT()函數結果

BEGIN

v_dno := &inputDeptno;

v_dna := '&inputname';

v_dloc := '&inputLoc';

--統計要增加的部門編號在dept表中的信息數量,如果返回0表示沒有此部門;

SELECT COUNT(deptno) INTO v_deptCount FROM dept WHERE deptno=v_dno;

IF v_deptCount >0 THEN

RAISE_APPLICATION_ERROR(-20888,'此部門編號已存在,請重新輸入');

ELSE

INSERT INTO dept(deptno,dname,loc) VALUES(v_dno,v_dna,v_dloc);

DBMS_OUTPUT.put_line('新部門增加成功');

COMMIT;

END IF;

EXCEPTION

WHEN others THEN

DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE) ;

DBMS_OUTPUT.put_line('SQLERRM = ' || SQLERRM) ;

ROLLBACK;

END;

/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM