Oracle Exception 異常處理

Oracle 異常處理

一、概述
異常分成三大類:預定義異常、非預定義異常、自定義異常
處理方法分爲:直接拋出異常、內部塊處理異常、遊標處理異常

預定義異常:由PL/SQL定義的異常。由於它們已在standard包中預定義了,因此,這些預定義異常可以直接在程序中使用,而不必再定義部分聲明。

非預定義異常:用於處理預定義異常所不能處理的Oracle錯誤。

自定義異常:用戶自定義的異常,需要在定義部分聲明後才能在可執行部分使用。用戶自定義異常對應的錯誤不一定是Oracle錯誤,例如它可能是一個數據錯誤。

三種異常中,預定義與非預定義異常都與Oracle錯誤有關,並且由Oracle隱含自動拋出,
而自定義異常與Oracle錯誤沒有任何關聯,由開發人員爲特定情況所定義的異常,需要顯式拋出(raise)。


二、預定義異常

1、預定義異常的種類
1.1、ACCESS_INTO_NULL
說明:對應於ORA-06530,當創建對象類型應用是,在引用對象屬性之前,如果沒有初始化對象,
直接爲對象屬性賦值,則出發此異常。
例子:create type emp_type as object(name varchar2(10),sal number(6,2));
declare
emp emp_type;
begin
emp.name:='scott';
exception
when ACCESS_INTO_NULL dbms_output.put_line('未初始化');
end;

1.2、CASE_NOT_FOUND
說明:對應於ORA-06592,編寫CASE語句時,如果在when子句中沒有包含必須的條件分支並且沒有包含else子句,就會出發此異常。

1.3、COLLECTION_IS_NULL
說明:對應於ORA-06531,在集合元素(嵌套表或VARRAY)賦值時,如果沒有初始化集合元素,則會出發此異常。
例子:declare
type ename_table_type is table of emp.ename%type;
ename_table ename_table_type;
begin
select ename into ename_table(2) from emp where empno=&no;
dbms_output.put_line(name_table(2));
exception
when COLLECTION_IS_NULL then dbms_output.put_ling('未初始化');
end;

1.4、CURSOR_ALREADY_OPEN
說明:ORA-06511,當打開已經打開的遊標時,會觸發此異常。

1.5、DUP_VAL_ON_INDEX
說明:對應於ORA-00001,當在唯一索引所對應的列上鍵入重複值時,會觸發此異常。

1.6、INVALID_CURSOR
說明:對應於ORA-01001,當試圖在不合法的遊標上執行操作時,會觸發此異常。例如從未打開的遊標提取數據或關閉未打開的遊標。

1.7、INVALID_NUMBER
說明:對應於ORA-01722,當內嵌sql語句不能有效地將字符轉變成數字時,會隱含地觸發此異常。

1.8、NO_DATA_FOUND
說明:對應於ORA-01403,當執行selct into未返回行,或者引用了索引表未初始化元素時,會觸發此異常。

1.9、TOO_MANY_ROWS
說明:對應於ORA-01422,當執行select into返回超過一行,則觸發此異常。

1.10、ZERO_DIVIDE
說明:對應於ORA-01476,當除數爲0時,觸發此異常。

1.11、SUBSCRIPT_BEYOND_COUNT
說明:對應於ORA-06533,當使用嵌套表或VARRAY元素時,如果元素下標超出範圍,會觸發此異常。
例子:declare
type emp_array_type is varray(20) of varchar2(10);
emp_array emp_array_type;
begin
emp_array:=emp_array_type('scott','mary');
dbms_output.put_line(emp_array(3));
exception
when SUBSCRIPT_BEYOND_COUNT then dbms_output.put_line('超出範圍');
end;

1.12、SUBSCRIPT_OUTSIDE_LIMT
說明:對應於ORA-06532,當使用嵌套表或VARRAY元素時,如果下標爲取負數,會觸發此異常。

1.13、VALUE_ERROR
說明:對應於ORA-06502,當執行賦值操作時,如果變量長度不足以容納實際數據,會觸發此異常。

1.14、LOGIN_DENIED
說明:對應於ORA-01017,當連接Oracle數據庫時,如果用戶名或密碼不正確,會觸發此異常。

1.15、NOT_LOGGED_ON
說明:對應於ORA-01012,如果在沒有連接到數據庫的情況下,執行PL/SQL塊,會觸發此異常。

1.16、PROGRAM_ERROR
說明:對應於ORA-06501,如果出現此錯誤,則表示存在PL/SQL內部問題,用戶此時可能需要重新安裝數據字典和PL/SQL系統包。

1.17、ROWTYPE_MISMATCH
說明:對應於ORA-06504,當執行賦值操作時,如果宿主遊標變量和PL/SQL遊標變量的返回類型不兼容,會觸發此異常。

1.18、SELF_IS_NULL
說明:對應於ORA-30625,當使用對象類型時,如果在null實例上調用成員方法,會觸發此異常。

1.19、STORAGE_ERROR
說明:對應於ORA-06500,PL/SQL塊運行時,如果超出內存空間或內存被損壞,會觸發此異常。

1.20、SYS_INVALID_ROWID
說明:對應於ORA-01410,當將字符轉變爲ROWID時,如果使用了無效的字符串,會觸發此異常。

1.21、TIMEOUT_ON_RESOURCE
說明:對應於ORA-00051,如果Oracle在等待資源時出現超時錯誤,會觸發此異常。

2、預定義異常的處理

說明:這裏說兩個常見的異常no_data_found和too_many_rows,這兩個異常多由select into語句觸發。
舉例:
declare
v_cnt number :=800;
v_name emp.ename%type;
begin
select ename into v_name from emp where sal=v_cnt;
dbms_output.put_line('姓名:' || v_name);
exception
when no_data_found then --直接拋出異常
dbms_output.put_line('不存在該工資值的僱員');
when too_many_rows then
dbms_output.put_line('存在多個僱員具有該工資');
when others then
rollback;
dbms_output.put_line('異常回滾退出');
end;

2.1、no_data_found異常
起因:給一個變量賦值時,查詢的結果爲空。
說明:如上面例子可以看到,一旦直接拋出異常,就會讓過程中斷。no_data_found這種異常,沒有嚴重到要讓程序中斷的地步,可以完全交給由程序進行處理。

2.1.1、使用內部獨立塊處理
說明:這是一種比較好的處理方式了,不會因爲這個異常而引起程序中斷。
例如:
declare
v_cnt:=800;
v_name emp.ename%type;
begin
begin
select ename into v_name from emp where sal=v_cnt;
exception
when no_data_found then
v_name:='';
end;
dbms_output.put_line('姓名:' || v_name);
exception
when too_many_rows then
dbms_output.put_line('存在多個僱員具有該工資');
when others then
rollback;
dbms_output.put_line('異常回滾退出');
end;

2.1.2、使用遊標處理
說明:遊標操作可以完全避免no_data_found異常。
例如:
declare
v_cnt:=800;
v_name emp.ename%type;
cursor c_cursor is select ename from emp where sal=v_cnt;
begin
open c_cursor;
fetch c_cursor into v_name;
close c_cursor;
dbms_output.put_line('姓名:' || v_name);
exception
when too_many_rows then
dbms_output.put_line('存在多個僱員具有該工資');
when others then
rollback;
dbms_output.put_line('異常回滾退出');
end;

2.2、too_many_rows異常
起因:給一個變量賦值時,查詢的結果有多條記錄或select into 語句中變量名與表名相同。
說明:返回多條記錄如果是可接受的,必須採用遊標處理;如果是不可接受的,必須採用內部快處理。

(1)、返回多條記錄如果是可接受的,則隨便取一條,用遊標處理。

(2)、返回多條記錄如果是不可接受的,則必須捕獲異常,用內部塊處理。
舉例:
declare
v_cnt:=800;
v_name emp.ename%type;
begin
begin
select ename into v_name from emp where sal=v_cnt;
exception
when no_data_found then
v_name:='';
when too_many_rows then
v_name:='';
dbms_output.put_line('存在多個僱員具有該工資');
end;
dbms_output.put_line('姓名:' || v_name);
exception
when others then
rollback;
dbms_output.put_line('異常回滾退出');
end;


三、非預定義異常
說明:非預定義異常用於處理與21個預定義異常無關的Oracle錯誤。
由於預定義異常只是與一部分Oracle錯誤相連的異常,所以如果要處理沒有與預定義異常對應的Oracle錯誤時,
則需要爲這些Oracle錯誤聲明相應的非預定義異常。聲明這樣的異常需要使用exception_init編譯指令。

exception_init編譯指令的定義如下:
pragma exception_init(exception_name,Oracle_error_number);
exception_name是預先被聲明的異常名,Oracle_error_number是錯誤號,這條命令必須寫在定義部分。

例子:
declare
e_inte exception; --定義
pragma exception_intt(e_inte,-2291); --關聯Oracle錯誤ORA-2291
begin
update emp set deptno=&dno where empno=&eno;
exception
when e_inte then
dbms_output.put_line('部門不存在');
end;

注意,通過exception_init,一個自定義異常只能和一個Oracle錯誤相連,在異常處理語句中,
sqlcode和sqlerrm將返回這個Oracle錯誤的代碼和消息文本,而不是返回用戶自定義消息


四、自定義異常

說明:自定義異常與Oracle錯誤沒有任何關聯,由開發人員爲特定情況所定義的異常,需要顯式拋出(raise)。

1、自定義異常的簡述
儘管自定義異常的聲明與變量的聲明類似,但異常是一個錯誤狀態,而不是一個數據項,
所以異常不能出現在賦值語句和sql語句中,但異常的作用域與定義部分其它變量的作用域相同。
如果一個自定義異常被傳遞到作用域外,則不能再通過原來的名字引用它。爲了解決這個問題,
我們可以在包中聲明異常,這個異常就可以在任何塊中使用,使用時在異常前加包名前綴即可。

自定義異常由raise語句產生(由exception_inti編譯指令聲明的用戶自定義異常也可通過對應的Oracle錯誤的出現而產生),
當然如果需要,預定義異常也可以使用raise語句來產生。

當一個異常產生是,控制權立即轉交給塊的異常處理部分。如果該塊沒有異常處理部分,
則向該塊的外一層塊傳遞。一旦控制權交給了異常處理部分,則再沒有辦法回到塊可執行部分。

一條異常處理語句可以處理多個異常,只要在when子句中由or分割多個異常即可。

如果塊中的異常沒有被處理,則該塊會帶着未處理的異常返回調用它的程序,這會導致調用它的程序出錯。
如果在存儲過程中出現異常,則存儲過程的out參數將得不到返回值。爲了避免未處理異常帶來的弊病,
我們最好在塊的最外層使用others子句處理塊中所有未處理的異常。這樣就可以確保所有的錯誤都能被發現和處理。

如果是定義部分的一個賦值語句產生了異常。即使在當前塊的異常處理部分中有處理該異常的處理語句,也不去執行,
而是立刻被傳遞到外部塊中。當異常傳遞到外部塊中以後,按照處理可執行部分中產生的異常一樣去處理該異常。

在異常處理語句中也可以產生異常,這個異常可以通過raise語句產生,或是由於出現一個運行錯誤而產生。
這兩種情況下產生的異常都被立刻傳遞到塊外,這與定義部分產生的異常一樣。爲什麼這樣處理呢?
因爲異常部分每一次只能有一個異常被處理,當一個異常被處理是,產生了另外一個異常,
而一次不能同時處理多個異常,所以將異常處理部分產生的異常傳遞到塊外。

2、自定義異常的簡單應用
說明:用戶定義異常類型,使用raise顯示拋出異常
declare
v_name:='mary';
v_dno:=80;
e_integrity exception; --定義自定義異常
e_no_rows exception;
pragma exception_init(e_integrity,-2291);
name emp.ename%type:=v_name;
dno emp.deptno%type:=v_dno;
begin
update emp set deptno=v_dno where ename=v_name;
if sql%notfound then
raise e_no_rows; --顯示拋出異常
end if;
exception
when e_integrity then
dbms_output.put_line('該部門不存在');
when e_no_rows then
dbms_output.put_line('該僱員不存在');
end;

3、raise拋出異常的三種方法
1)、Raise exception:用於拋出當前程序中定義的異常或在 standard 中的系統異常。
2)、Raise package.exception:用於拋出有一些異常是定義在非標準包中的,如UTL_FILE,DBMS_SQL以及程序員創建的包中異常
3)、Raise:不帶任何參數,這種情況只出現在希望將當前的異常傳到外部程序時。


五、異常的函數

說明:當出現異常,通過使用異常函數可以取得錯誤號及相關錯誤信息,另外通過使用raise_application_error也可自定義錯誤號與錯誤信息。

1、sqlcode和sqlerrm函數

sqlcode返回異常對象的錯誤代碼號,sqlerrm返回的是對應的錯誤信息,爲了在plsql中處理其他未預料的Oracle錯誤,
可以在異常處理部分的when others子句後引用這兩個函數來確定錯誤號和信息。

異常種類 SQLCODE SQLERRM
Oracle錯誤對應的異常 負數 Oracle錯誤
NO_DATA_FOUND +100 No data found
自定義異常 -1 User-Defined Exception
沒有產生異常 0 Oracle-0000

注意:如果使用exception_init預編譯指令聲明與Oracle錯誤相連的自定義異常,則SQLCODE和SQLERRM返回
對應的Oracle錯誤代碼和相應的錯誤信息,而不是返回+1和User-Defined。

如果SQLERRM是可以帶一個數字參數,返回值是與這個數字參數相關的文本。

如果要在sql語句中使用sqlcode和sqlerrm,則一定要先把它們的值賦給局部變量,然後再將這些局部變量用在sql語句中。
因爲這些函數是過程性的,不能直接用在sql語句中。

EXCEPTION
WHEN OTHERS
THEN
ROLLBACK;
v_message := '錯誤行號:' || DBMS_UTILITY.format_error_backtrace () || '錯誤代碼:'|| SQLCODE|| '錯誤提示'|| SQLERRM;
DBMS_OUTPUT.put_line (v_message);
END;

2、raise_application_error
說明:該過程用於自定義錯誤信息,僅限數據庫端子程序使用(過程、函數、包、觸發器),不能在匿名塊或客戶端子程序中。。
語法:raise_application_error(error_number,mesage[,[true|false]]);
其中error_number定義錯誤號,必輸是-20000到-20999之間的負整數;message指定錯誤信息,不長於2048字節;
第三個爲可選參數,true則錯誤會被放在先前錯誤的堆棧中,fale則替換先前所有錯誤,默認爲false。

例子:
create or replace procedure raise_comn(eno number,commission number) is
v_comm emp.comm%type;
begin
select comm into v_comm from emp where empno=eno;
if v_comm is null then
raise_application_error(-20001,'該僱員無補助');
end if;
exception
when no_date_found then
dbms_output.put_line('僱員不存在');
end;


六、PL/SQL編譯警告
說明:10g後新增功能,爲了提高plsql子程序的健壯性並避免運行錯誤,可以激活警告檢查功能。

1、PL/SQL警告的分類
severs:檢查可能出現的不可預料的結果或錯誤結果,例如參數別名。
performance:檢查可能引起的性能問題,例如執行insert時爲number列提供varchar2數據。
informational:檢查子程序中的死代碼。
all:檢查所有警告(上面3種都檢查)。

2、PL/SQL警告消息的控制
說明:爲了激活警告功能,需要設置初始化參數PLSQL_WARNINGS。
可以通過系統級、會話級、DBMS_VARNINGS系統包、ALTER PROCEDURE命令設置。
可以激活或禁止所有警告或某種警告。

alter system set plsql_warnings='enable:all'; --系統級
alter session set plsql_warnings='enable:performance'; --會話級
call dbms_warning.set_warning_setting_string('enable:severs','session'); --DBMS_VARNINGS系統包
alter procedure hello compile plsql_warnings='enable:performance'; --ALTER PROCEDURE命令
alter session set plsql_warnings='disable:all';

3、PL/SQL警告的使用

1)、檢測死代碼
create or replace procedure dead_code as
x number :=10;
begin
if x=10 then x:=20;
else
x:=100; --死代碼,永遠不會執行
end if;
end dead_code;

alter session set plsql_warnings='enable:informational';--激活
alter procedure dead_code compile;--編譯
show errors;--顯示

2)、檢測引起性能問題的代碼
create or replace precedure update_sql(name varchar2,salary varchar2) is
begin
update emp set sql=salary where ename=name;
end;

alter session set plsql_warnings='enable:informational';--激活
alter procedure dead_code compile;--編譯
show errors;--顯示

發佈了65 篇原創文章 · 獲贊 24 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章