- Oracle 11g從入門到精通(第2版) (軟件開發(fā)視頻大講堂)
- 明日科技
- 3831字
- 2020-11-28 15:54:59
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字段的值不許為空。
- JavaScript前端開發(fā)模塊化教程
- Software Testing using Visual Studio 2012
- BeagleBone Media Center
- OpenNI Cookbook
- React.js Essentials
- Java Web應(yīng)用開發(fā)技術(shù)與案例教程(第2版)
- Python貝葉斯分析(第2版)
- Unity 5.x By Example
- Hands-On Enterprise Automation with Python.
- The Complete Coding Interview Guide in Java
- PHP從入門到精通(第4版)(軟件開發(fā)視頻大講堂)
- GitHub入門與實(shí)踐
- Learning Concurrency in Python
- Mastering Clojure
- C/C++程序設(shè)計(jì)教程