官术网_书友最值得收藏!

5.5 PL/SQL異常處理

視頻講解:光盤\TM\lx\5\PL/SQL異常處理.mp4

5.5.1 異常處理方法

在編寫PL/SQL程序時(shí),不可避免地會(huì)發(fā)生一些錯(cuò)誤,可能是程序設(shè)計(jì)人員自己造成的,也可能是操作系統(tǒng)或硬件環(huán)境出錯(cuò),比如出現(xiàn)除數(shù)為零、磁盤I/O錯(cuò)誤等情況。對(duì)于出現(xiàn)的這些錯(cuò)誤,Oracle采用異常機(jī)制來處理,異常處理代碼通常放在PL/SQL的EXCEPTION代碼塊中。根據(jù)異常產(chǎn)生的機(jī)制和原理,可將Oracle系統(tǒng)異常分為以下兩大類:

預(yù)定義異常:Oracle系統(tǒng)自身為用戶提供了大量的、可在PL/SQL中使用的預(yù)定義異常,以便檢查用戶代碼失敗的一般原因。它們都定義在Oracle的核心PL/SQL庫中,用戶可以在自己的PL/SQL異常處理部分使用名稱對(duì)其進(jìn)行標(biāo)識(shí)。對(duì)這種異常情況的處理,用戶無需在程序中定義,它們由Oracle自動(dòng)引發(fā)。

自定義異常:有時(shí)候可能會(huì)出現(xiàn)操作系統(tǒng)錯(cuò)誤或機(jī)器硬件故障,這些錯(cuò)誤Oracle系統(tǒng)自身無法知曉,也不能控制。例如,操作系統(tǒng)因病毒破壞而產(chǎn)生故障、磁盤損壞、網(wǎng)絡(luò)突然中斷等。另外,因業(yè)務(wù)的實(shí)際需求,程序設(shè)計(jì)人員需要自定義一些錯(cuò)誤的業(yè)務(wù)邏輯,而PL/SQL程序在運(yùn)行過程中就可能會(huì)觸發(fā)到這些錯(cuò)誤的業(yè)務(wù)邏輯。那么,對(duì)于以上這些異常情況的處理,就需要用戶在程序中自定義異常,然后由Oracle自動(dòng)引發(fā)。

異常的處理方法分為以下兩種。

1.預(yù)定義異常處理方法

每當(dāng)PL/SQL程序違反了Oracle的規(guī)則或超出系統(tǒng)的限制時(shí),系統(tǒng)就自動(dòng)地產(chǎn)生內(nèi)部異常。每個(gè)Oracle異常都有一個(gè)號(hào)碼,但異常必須按名處理。因此,PL/SQL對(duì)那些常見的異常預(yù)定義了異常名。

2.預(yù)定義異常和用戶自定義異常處理方法

異常聲明:

用戶定義異常包括預(yù)定義異常和用戶自定義異常,用戶定義的異常只能在PL/SQL塊的聲明部分進(jìn)行聲明。聲明方式與變量聲明類似。

拋出異常:

用戶定義的異常使用RAISE語句顯式地提出。

為內(nèi)部異常命名:

在PL/SQL中,必須使用OTHERS處理程序或用偽命令EXCEPTION_INIT來處理未命名的內(nèi)部異常。EXCEPTION_INIT的作用是告訴編譯程序?qū)⒁粋€(gè)異常名與一個(gè)Oracle錯(cuò)誤號(hào)碼聯(lián)系起來。因此,用戶就可以按名稱引用任何內(nèi)部異常,并為它編寫一個(gè)特定的處理程序。

注意

異常是一種狀態(tài)而不是一個(gè)對(duì)象,因此,異常名不能出現(xiàn)在賦值語句或SQL語句中。PRAGMA EXCEPTION_INIT的作用是將一個(gè)異常名與一個(gè)Oracle錯(cuò)誤號(hào)碼聯(lián)系起來。因此,用戶就可以按名稱引用任何內(nèi)部異常,并為它編寫一個(gè)特定的處理程序。

5.5.2 異常處理語法

1.聲明異常

語法:

        exception_name EXCEPTION;

其中,exception_name為用戶定義的異常名。

2.為內(nèi)部異常命名

        PRAGE EXCEPTION_INIT(exception_name, ORA_errornumber);

其中,ORA_errornumber為用戶定義的Oracle錯(cuò)誤號(hào)。

3.異常定義

代碼如下所示。

        DECLARE
            exceprion_name EXCEPTION;
        BEGIN
            IF condition THEN
                RAISE exception_name;
            END IF;
            EXCEPTION
                WHEN exception_name THEN
                Statement;
        END;

4.異常處理

代碼如下所示。

        SET SERVEROUTPUT ON  --將輸出流開關(guān)打開
        EXCEPTION
            WHEN exception1 THEN
                statement1
            WHEN exception2 THEN
                statement2
            ……
            WHEN OTHERS THEN
                statement3

5.使用SQLCODE和SQLERRM函數(shù)定義提示信息

        DBMS_OUTPUT.PUT_LINE(’錯(cuò)誤號(hào):'||SQLCODE);
        DBMS_OUTPUT.PUT_LINE(’錯(cuò)誤號(hào):'||SQLERRM);

5.5.3 預(yù)定義異常

當(dāng)PL/SQL程序違反了Oracle系統(tǒng)內(nèi)部規(guī)定的設(shè)計(jì)規(guī)范時(shí),就會(huì)自動(dòng)引發(fā)一個(gè)預(yù)定義的異常,例如,當(dāng)除數(shù)為零時(shí),就會(huì)引發(fā)ZERO_DIVIED異常。Oracle系統(tǒng)常見的預(yù)定義異常標(biāo)識(shí)符如下:

ACCESS_INTO_NULL:該異常對(duì)應(yīng)于ORA-06530錯(cuò)誤。為了引用對(duì)象屬性,必須首先初始化對(duì)象。當(dāng)直接引用未初始化的對(duì)象屬性時(shí),會(huì)觸發(fā)該異常。

CASE_NOT_FOUND:該異常應(yīng)用于ORA-06592錯(cuò)誤。當(dāng)CASE語句的WHEN子句沒有包含必須條件分支或者ELSE子句時(shí),會(huì)觸發(fā)該異常。

COLLECTION_IS_NULL:該異常應(yīng)用于ORA-06531錯(cuò)誤。在給嵌套表變量或者VARRAY變量賦值之前,必須首先初始化集合變量。如果沒有初始化集合變量,會(huì)觸發(fā)該異常。

CURSOR_ALREADY_OPEN:該異常應(yīng)用于ORA-06511錯(cuò)誤。當(dāng)在已打開游標(biāo)上執(zhí)行OPEN操作時(shí),會(huì)觸發(fā)該異常。

INVALID_CURSOR:該異常應(yīng)用于ORA-01001錯(cuò)誤。當(dāng)視圖從未打開游標(biāo)提取數(shù)據(jù),或者關(guān)閉未打開游標(biāo)時(shí),會(huì)觸發(fā)該異常。

INVALID_NUMBER:該異常應(yīng)用于ORA-01722錯(cuò)誤。當(dāng)內(nèi)嵌SQL語句不能將字符轉(zhuǎn)變成數(shù)字時(shí),會(huì)觸發(fā)該異常。

LOGIN_DENIED:該異常應(yīng)用于ORA-01017錯(cuò)誤。當(dāng)連接到Oracle數(shù)據(jù)庫時(shí),如果提供了不正確的用戶名或者口令,會(huì)觸發(fā)該異常。

NO_DATA_FOUND:該異常應(yīng)用于ORA-01403錯(cuò)誤。當(dāng)執(zhí)行SELECT INTO未返回行,或者引用了未初始化的PL/SQL表元素時(shí),會(huì)觸發(fā)該異常。

NOT_LOGGED_ON:該異常應(yīng)用于ORA-01012錯(cuò)誤。如果沒有連接到Oracle數(shù)據(jù)庫,當(dāng)執(zhí)行內(nèi)嵌SQL語句時(shí),會(huì)觸發(fā)該異常。

PROGRAM_ERROR:該異常應(yīng)用于ORA-06501錯(cuò)誤。如果出現(xiàn)該錯(cuò)誤,則表示存在PL/SQL內(nèi)部問題,在這種情況下需要重新安裝數(shù)據(jù)字典視圖和PL/SQL包。

ROWTYPE_MISMATCH:該異常應(yīng)用于ORA-016504錯(cuò)誤。當(dāng)執(zhí)行賦值操作時(shí),如果宿主變量和游標(biāo)變量具有不兼容的返回類型,會(huì)觸發(fā)該異常。

SELF_IS_NULL:該異常應(yīng)用于ORA-30625錯(cuò)誤。當(dāng)使用對(duì)象類型時(shí),如果在NULL實(shí)例上調(diào)用成員方法,會(huì)觸發(fā)該異常。

STORAGE_ERROR:該異常應(yīng)用于ORA-06500錯(cuò)誤。當(dāng)執(zhí)行PL/SQL塊時(shí),如果超出內(nèi)存空間或者內(nèi)存被破壞,會(huì)觸發(fā)該異常。

SUBSCRIPT_BEYOND_COUNT:該異常應(yīng)用于ORA-06533錯(cuò)誤。當(dāng)使用嵌套表或者VARRAY元素時(shí),如果下標(biāo)超出了嵌套表或者VARRAY元素的范圍,會(huì)觸發(fā)該異常。

SUBSCRIPT_OUTSIDE_LIMIT:該異常應(yīng)用于ORA-06532錯(cuò)誤。當(dāng)使用嵌套表或者VARRAY元素時(shí),如果元素下標(biāo)為負(fù)值,會(huì)觸發(fā)該異常。

SYS_INVALID_ROWID:該異常應(yīng)用于ORA-01410錯(cuò)誤。當(dāng)將字符串轉(zhuǎn)變?yōu)镽OWID時(shí),如果使用了無效字符串,會(huì)觸發(fā)該異常。

TIMEOUT_ON_RESOURCE:該異常應(yīng)用于ORA-00051錯(cuò)誤。當(dāng)?shù)却Y源時(shí)如果出現(xiàn)超時(shí)錯(cuò)誤,會(huì)觸發(fā)該異常。

TOO_MANY_ROWS:該異常應(yīng)用于ORA-01422錯(cuò)誤。當(dāng)執(zhí)行SELECT INTO語句時(shí),如果返回超過一行,會(huì)觸發(fā)該異常。

VALUE_ERROR:該異常應(yīng)用于ORA-06502錯(cuò)誤。當(dāng)執(zhí)行賦值操作時(shí),如果變量長(zhǎng)度不足以容納實(shí)際數(shù)據(jù),會(huì)觸發(fā)該異常。

ZERO_DIVIDE:該異常應(yīng)用于ORA-01476錯(cuò)誤。如果用數(shù)字值除以0,會(huì)觸發(fā)該異常。

下面通過一個(gè)實(shí)例來說明如何使用系統(tǒng)預(yù)定義異常。

【例5.34】 使用SELECT INTO語句檢索emp表中部門編號(hào)為10的雇員記錄信息,然后使用“too_many_rows”預(yù)定義異常捕獲錯(cuò)誤信息并輸出,代碼如下(實(shí)例位置:光盤\TM\sl\5\17)

        SQL> set serveroutput on
        SQL> declare
          2   var_empno number;                    --定義變量,存儲(chǔ)雇員編號(hào)
          3   var_ename varchar2(50);              --定義變量,存儲(chǔ)雇員名稱
          4  begin
          5   select empno, ename into var_empno, var_ename
          6   from emp
          7   where deptno=10;                     --檢索部門編號(hào)為10的雇員信息
          8   if sql%found then                    --若檢索成功,則輸出雇員信息
          9     dbms_output.put_line(’雇員編號(hào):'||var_empno||';雇員名稱’||var_ename);
         10   end if;
         11  exception                             --捕獲異常
         12   when too_many_rows then              --若SELECT INTO語句的返回記錄超過一行
         13     dbms_output.put_line(’返回記錄超過一行’);
         14   when no_data_found then              --若SELECT INTO語句的返回記錄為0行
         15     dbms_output.put_line(’無數(shù)據(jù)記錄’);
         16  end;
         17  /

本例運(yùn)行結(jié)果如圖5.18所示。

圖5.18 使用too_many_rows異常

在上面的例子中,由于部門編號(hào)為10的員工記錄數(shù)大于1,所以SELECT INTO語句的返回行數(shù)就要超過一行,由于Oracle系統(tǒng)內(nèi)部規(guī)定不允許該語句的返回行數(shù)超過一行,所以必然會(huì)引發(fā)異常,即引發(fā)too_many_rows系統(tǒng)預(yù)定義異常。

5.5.4 自定義異常

Oracle系統(tǒng)內(nèi)部的預(yù)定義異常僅僅20個(gè)左右,而實(shí)際程序運(yùn)行過程中可能會(huì)產(chǎn)生幾千種異常情況,為此Oracle經(jīng)常使用錯(cuò)誤編號(hào)和相關(guān)描述輸出異常信息。另外,程序設(shè)計(jì)人員可以根據(jù)實(shí)際的業(yè)務(wù)需求定義一些特殊異常,這樣Oracle的自定義異常就可以分為錯(cuò)誤編號(hào)異常和業(yè)務(wù)邏輯異常兩種。

1.錯(cuò)誤編號(hào)異常

錯(cuò)誤編號(hào)異常是指在Oracle系統(tǒng)發(fā)生錯(cuò)誤時(shí),系統(tǒng)會(huì)顯示錯(cuò)誤號(hào)和相關(guān)描述信息的異常。雖然直接使用錯(cuò)誤編號(hào)也可以完成異常處理,但錯(cuò)誤編號(hào)較為抽象,不易于用戶理解和記憶,對(duì)于這種異常,首先在PL/SQL塊的聲明部分(DECLARE部分)使用EXCEPTION類型定義一個(gè)異常變量名,然后使用語句PRAGMA EXCEPTION_INIT為“錯(cuò)誤編號(hào)”關(guān)聯(lián)“這個(gè)異常變量名”,接下來就可以像對(duì)待系統(tǒng)預(yù)定義異常一樣處理了。

下面通過一個(gè)具體的實(shí)例來演示如何為Oracle系統(tǒng)的“錯(cuò)誤編號(hào)”做自定義異常處理。首先我們向dept表中插入一條部門編號(hào)為10的記錄(事先查詢過,部門編號(hào)10已經(jīng)存在于dept表中,并且部門編號(hào)為dept表的唯一主鍵),然后執(zhí)行INSERT語句,得到如圖5.19所示的運(yùn)行結(jié)果。

圖5.19 因主鍵值重復(fù)而顯示的錯(cuò)誤編號(hào)

從圖5.19所示的運(yùn)行結(jié)果中可以看到,程序執(zhí)行中斷而崩潰掉了,并顯示錯(cuò)誤信息為“ORA-00001”—即錯(cuò)誤編號(hào)為“00001”,那么對(duì)于Oracle捕獲到的這個(gè)異常可以通過如下實(shí)例來解決。

【例5.35】 定義錯(cuò)誤編號(hào)為“00001”的異常變量,然后向dept表中插入一條能夠“違反唯一約束條件”的記錄,最后在exception代碼體中輸出異常提示信息,代碼如下(實(shí)例位置:光盤\TM\sl\5\18)

        SQL> set serveroutput on
        SQL> declare
          2   primary_iterant exception;                       --定義一個(gè)異常變量
          3   pragma exception_init(primary_iterant, -00001);  --關(guān)聯(lián)錯(cuò)誤號(hào)和異常變量名
          4  begin
          5   /*向dept表中插入一條與已有主鍵值重復(fù)的記錄,以便引發(fā)異常*/
          6   insert into dept values(10, ’軟件開發(fā)部’, ’深圳’);
          7  exception
          8   when primary_iterant then                        --若Oracle捕獲到的異常為-0001異常
          9     dbms_output.put_line(’主鍵不允許重復(fù)!');      --輸出異常描述信息
         10  end;
         11  /

本例運(yùn)行結(jié)果如圖5.20所示。

圖5.20 定義主鍵值重復(fù)的異常

通過運(yùn)行結(jié)果可以看到,使用異常處理機(jī)制,可以防止Oracle系統(tǒng)因引發(fā)異常而導(dǎo)致程序崩潰,使程序有機(jī)會(huì)自動(dòng)糾正錯(cuò)誤,而且自定義異常容易理解和記憶,方便用戶的使用。

2.業(yè)務(wù)邏輯異常

在實(shí)際的應(yīng)用中,程序開發(fā)人員可以根據(jù)具體的業(yè)務(wù)邏輯規(guī)則自定義一個(gè)異常。這樣,當(dāng)用戶操作違反業(yè)務(wù)邏輯規(guī)則時(shí),就引發(fā)一個(gè)自定義異常,從而中斷程序的正常執(zhí)行并轉(zhuǎn)到自定義的異常處理部分。

無論是預(yù)定義異常,還是錯(cuò)誤編號(hào)異常,都是由Oracle系統(tǒng)判斷的錯(cuò)誤,但業(yè)務(wù)邏輯異常是Oracle系統(tǒng)本身無法知道的,這樣就需要有一個(gè)引發(fā)異常的機(jī)制,引發(fā)業(yè)務(wù)邏輯異常通常使用RAISE語句來實(shí)現(xiàn)。當(dāng)引發(fā)一個(gè)異常時(shí),控制就會(huì)轉(zhuǎn)到EXCEPTION異常處理部分執(zhí)行異常處理語句。業(yè)務(wù)邏輯異常首先要在DECLARE部分使用EXCEPTION類型聲明一個(gè)異常變量,然后在BEGIN部分根據(jù)一定的業(yè)務(wù)邏輯規(guī)則執(zhí)行RAISE語句(在RAISE關(guān)鍵字后面跟著異常變量名),最后在EXCEPTION部分編寫異常處理語句。下面通過一個(gè)實(shí)例來演示如何定義和引發(fā)“業(yè)務(wù)邏輯異常”。

【例5.36】 自定義一個(gè)異常變量,在向dept表中插入數(shù)據(jù)時(shí),若判斷l(xiāng)oc字段的值為null,則使用RAISE語句引發(fā)異常,并將程序的執(zhí)行流程轉(zhuǎn)入到EXCEPTION部分進(jìn)行處理,代碼如下(實(shí)例位置:光盤\TM\sl\5\19)

        SQL> set serveroutput on
        SQL> declare
          2   null_exception exception;                    --聲明一個(gè)exception類型的異常變量
          3   dept_row dept%rowtype;                       --聲明rowtype類型的變量dept_row
          4  begin
          5   dept_row.deptno:=66;                         --給部門編號(hào)變量賦值
          6   dept_row.dname:=’公關(guān)部’;                   --給部門名稱變量賦值
          7   insert into dept
          8   values(dept_row.deptno, dept_row.dname, dept_row.loc); --向dept表中插入一條記錄
          9   if dept_row.loc is null then                  --如果判斷“l(fā)oc”變量的值為null
         10     raise null_exception;                       --引發(fā)null異常,程序轉(zhuǎn)入exception部分
         11   end if;
         12  exception
         13   when null_exception then                      --當(dāng)raise引發(fā)的異常是null_exception時(shí)
         14   dbms_output.put_line('loc字段的值不許為null'); --輸出異常提示信息
         15   rollback;                                     --回滾插入的數(shù)據(jù)記錄
         16  end;
         17  /

本例運(yùn)行結(jié)果如圖5.21所示。

圖5.21 業(yè)務(wù)邏輯異常

說明

使用desc命令查看dept表的設(shè)計(jì)情況,可以看到loc字段允許為null,但實(shí)際應(yīng)用中l(wèi)oc字段的值(部門位置)可能會(huì)被要求必須填寫,這樣程序設(shè)計(jì)人員就可以通過自定義業(yè)務(wù)邏輯異常來限制loc字段的值不許為空。

主站蜘蛛池模板: 嘉鱼县| 龙门县| 八宿县| 阿城市| 德州市| 沂源县| 江都市| 公安县| 松阳县| 巩留县| 四会市| 阿克陶县| 周至县| 洛隆县| 榆社县| 广平县| 资中县| 新建县| 体育| 和林格尔县| 新绛县| 宁海县| 沈阳市| 大方县| 曲阜市| 永安市| 南安市| 紫阳县| 吴川市| 屏东县| 天台县| 房山区| 昌邑市| 玛纳斯县| 平武县| 拉萨市| 白水县| 竹北市| 沿河| 灯塔市| 景宁|