1. 程式人生 > >Oracle_PL/SQL(9) 例外處理

Oracle_PL/SQL(9) 例外處理

過程 用戶 sql ble sub app 賦值 sda ati

例外處理
1.例外分類:預定義例外,非預定義例外,自定義例外三種
傳遞例外:如果在例外處理部分exception沒有捕捉例外,oracle會將例外傳遞到調用環境.
捕捉並處理例外:使用例外處理部分完成
exception
when exception1 then
statement1;
when exception2 then
statement2;
...
when others then --必須是例外處理部分的最後一條子句
statement_n;
...

2.預定義例外
2.1系統預定義例外有21個,如下:
dup_val_on_index:ora-00001錯誤。
當在唯一索引所對應的列上鍵入重復值時,觸發例外

zero_divide:ora-01476錯誤。
如果使用數字值除0,則會隱含觸發例外
invalid_number:ora-01722錯誤。
當sql語句不能有效的將字符轉變成數字時,會隱含觸發例外
no_date_found:ora-01403錯誤。
當執行select into 未返回行,或者引用了索引表未初始化的元素時,會隱含觸發例外
too_many_rows:ora-01422錯誤。
當執行select into 語句時,如果返回超過一行,則會觸發例外
access_into_null:ora-06530錯誤。
在引用對象屬性之前,必須首先初始化對象,否則觸發例外
value_error:ora-06502錯誤。
如果變量長度不足以容納實際數據,則會隱含的出發例外
case_not_found:ora-06592錯誤。
在編寫case語句時,如果在when子句中沒有包含必須的條件分支(else),
並且沒有包含else子句,就會觸發
cursor_already_open:ora-06511錯誤。
當重新打開已經打開的遊標時,會隱含的觸發例外.
已經使用open打開了顯示遊標,並執行for循環,就會隱含的觸發該例外
invalid_cursor:ora-01001錯誤。
當試圖在不合法的遊標上執行操作時,會隱含的觸發例外.
要從未打開的遊標提取數據,或者關閉未打開的遊標,則觸發例外
rowtype_mismatch:ora-06504錯誤。
宿主遊標變量和pl/sql遊標變量的返回類型不兼容
collection_is_null:ora-06531錯誤。
在給集合元素(嵌套表和varray類型)賦值前,必須首先初始化集合元素,否則觸發例外
subscript_beyond_count:ora-06533錯誤。
當使用嵌套表或varray元素時,如果元素下標超出了嵌套表或varray元素的範圍,
則回隱含的觸發例外
subscript_outside_limit:ora-06532錯誤。
當使用嵌套表或varray元素時,如果元素下標為負值,則會隱含觸發例外
login_denied:ora-01017錯誤。
連接數據庫時,提供了不正確的用戶名和口令
not_logged_on:ora-01012錯誤。
沒有連接到數據庫
program_error:ora-06501錯誤。
存在pl/sql內部問題,可能需要重新安裝數據字典和pl/sql系統包
self_is_null:ora-30625錯誤。
使用對象類型時,如果在null實例上調用成員方法,則會隱含觸發例外
storage_error:ora-06500錯誤。
如果超出內存或者內存被損壞
sys_invalid_rowid:ora-01410錯誤。
當字符串轉變為rowid時,必須使用有效的字符串,否則觸發例外
timeout_on_resource:ora-00051錯誤。
oracle在等待資源時出現超時錯誤


2.2 舉例
例1: case_not_found:
ora-06592.在編寫CASE語句時,如果在WHEN子句中沒有包含必須的條件分支(else),
並且沒有包含ELSE子句,就會觸發
declare
v_sal emp.sal%type;
begin
select sal into v_sal from emp where empno=&no;
case
when v_sal<1000 then
update emp set sal=sal+100 where empno=&no;
when v_sal<2000 then
update emp set sal=sal+150 where empno=&no;
when v_sal<3000 then
update emp set sal=sal+200 where empno=&no;
end case;
--exception
-- when case_not_found then
-- dbms_output.put_line(‘在CASE語句中缺少與‘||v_sal||‘相關的條件‘);
end;

例2: 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(1) from emp where empno=&no;
dbms_output.put_line(‘雇員名:‘||ename_table(1));
exception
when collection_is_null then
dbms_output.put_line(‘必須使用構造方法初始化集合元素‘);
when subscript_beyond_count then
dbms_output.put_line(‘下標超出了集合元素的範圍‘);
end;

3.處理非預定義例外
使用非預定義例外包括三步:
在定義部分定義例外名,
然後在例外和ORACLE錯誤之間建立關聯,
最終在例外處理部分捕捉並處理例外.
當定義oracle錯誤和例外之間的關聯關系時,需要使用偽過程exception_init
update emp set deptno=99 where empno=7788;
ORA-02291

declare
e_integrity exception;
pragma exception_init(e_integrity,-2291);
begin
update emp set deptno=&dno where empno=&eno;
exception
when e_integrity then
dbms_output.put_line(‘該部門不存在‘);
end;

4.處理自定義例外
自定義例外與oracle錯誤沒有任何關聯。
與預定義和非預定義不同,自定義例外必須顯示觸發。
declare
e_no_employee exception;
begin
update emp set deptno=20 where empno=&eno;
if sql%notfound then
raise e_no_employee;
end if;
exception
when e_no_employee then
dbms_output.put_line(‘該雇員不存在‘);
end;

5.使用例外函數
函數sqlcode用於取得oracle錯誤號,而sqlerrm則用於取得與之相關的錯誤信息。
在存儲過程、函數、包中可以使用raise_application_error自定義錯誤號和錯誤消息。
5.1 sqlcode和sqlerrm
declare
v_ename emp.ename%type;
-- v_code varchar2(100);
-- v_errm varchar2(100);
begin
select ename into v_ename from emp where empno=&empno;
--exception
-- when others then
-- dbms_output.put_line(‘錯誤號:‘||sqlcode);
-- dbms_output.put_line(‘錯誤信息:‘||sqlerrm);
-- v_code:=sqlcode;
-- v_errm:=sqlerrm;
-- insert into error_log (id,code,errm,e_date)
-- values (seq_log.nextval,v_code,v_errm,sysdate);
end;

5.2 raise_application_error
用於在PL/SQL應用程序中自定義錯誤消息。
raise_application_error只能在過程,函數,包,觸發器中使用,不能在匿名塊中使用。
語法:raise_application_error(error_number,message);
說明:
error_number:必須在-20000和-20999之間的負整數.
message:最大2048字節
舉例:
create or replace procedure proc_trans_value(p_acid_out number,p_acid_in number,p_value number)
is
l_cnt number(8):=0;
l_value account.value%type;
begin
select count(1) into l_cnt from account where accountid=p_acid_out;
if l_cnt=1 then
select value into l_value from account where accountid=p_acid_out;
if l_value>=p_value then
update account set value=value-p_value where accountid=p_acid_out;
else
raise_application_error(-20003,‘[轉出賬戶金額不足]‘);
end if;
else
raise_application_error(-20001,‘[轉出賬戶不存在]‘);
end if;
select count(1) into l_cnt from account where accountid=p_acid_in;
if l_cnt=1 then
update account set value=value+p_value where accountid=p_acid_in;
else
raise_application_error(-20002,‘[轉入賬戶不存在]‘);
end if;
end proc_trans_value;

insert into account values (111,‘a‘,10000);
insert into account values (112,‘b‘,100);
exec proc_trans_value(111,112,20000)

Oracle_PL/SQL(9) 例外處理