Oracle SQL 異常處理
今天學了異常處理
raise_application_error()函式
raise_application_error() 只能把異常丟擲而不能處理異常。預定義異常包括
– NO_DATA_FOUND --沒有找到資料
– TOO_MANY_ROWS --找到多行資料
– INVALID_CURSOR --失效的遊標
– ZERO_DIVIDE --除數為零
– DUP_VAL_ON_INDEX –唯一索引中插入了重複值
預定義異常的示例:
declare v_id emp.empno%type;--宣告變數 begin select empno into v_id from emp where deptno =40; exception--異常處理 when no_data_found then--no_data_found 是使用 select 某欄位,然後 into 的時候,該欄位沒有出。 rollback; dbms_output.put_line('沒有40號部門記錄'); when too_many_rows then--too_many_rows 是使用 select 某欄位,然後 into 的時候,該欄位有多個值。 rollback; dbms_output.put_line('返回多條記錄'); when others then--其它的異常出現 rollback; dbms_output.put_line('出現其他錯誤.'); end;
使用者自定義異常就是使用者定義一個異常情況,遇到這種情況再對這種情況進行處理
因為使用者定義的異常不一定是Oracle返回的系統錯誤,系統不會自動觸發,需要在宣告部分定義。使用者定義的異常處理部分基本上和預定義異常相同。
declare salary_levelvarchar2(1); invalid_salary_levelexception; --宣告異常 begin salary_level := 'D'; if salary_level not in ('A','B','C') then raise invalid_salary_level;--觸發異常 end if; exception--異常處理 when invalid_salary_level then dbms_output.put_line('invalid salary level'); end;
raise_application_error() 函式只是將異常丟擲,不進行異常處理,並且終止程式。而使用者自定義異常以及預定義異常不回終止程式,但會終止該 PL/SQL 程式碼塊,所以一個儲存過程中可以有多個 PL/SQL 程式碼塊。
關於異常的語法及定義:
什麼是異常:
PL/SQL用異常和異常處理器來實現錯誤處理
Oracle中出現錯誤的情形通常分為編譯時錯誤(compile-time error)和執行時錯誤(run-time error)。
異常在PL/SQL執行過程中很可能出現
對異常如果不進行處理,異常可能會中斷程式的執行
捕獲異常的規則:
在異常部分WHEN 子句沒有數量限制
當異常丟擲後,控制無條件轉到異常處理部分
EXCEPTION 關鍵詞開始異常處理部分 WHEN OTHERS 為最後的一條子句
在異常塊中,只有一個控制代碼會處理異常
關於異常捕獲的函式:
SQLCODE 返回錯誤程式碼
SQLERRM 返回與錯誤程式碼關聯的訊息
儲存任何非預期的異常的錯誤編碼和錯誤訊息
declare v_error_codeNUMBER; v_error_messageVARCHAR2(255); BEGIN EXCEPTION WHEN OTHERS THEN ROLLBACK; v_error_code := SQLCODE; v_error_message := SQLERRM; INSERT INTO err_logs VALUES (v_error_code,v_error_message); END;
異常的傳播
PL/SQL中錯誤處理的步驟:
步驟1:如果當前塊中有該異常的處理器,則執行該異常處理語句塊,然後控制權傳遞到外層語句塊 步驟2:如果沒有當前異常的處理器,把該異常傳播給外層塊。然後在外層執行步驟1:如果此語句在最外層語句塊,則該異常將被傳播給呼叫環境
沒有處理的異常將沿檢測異常呼叫程式傳播到外面,當異常被處理並解決或到達程式最外層傳播停止。異常是自裡向外逐級傳遞的。
小題:
1.根據員工號,獲得員工到目前為止參加工作年限(保留到整數),員工號不存在時提示“此員工號不存在”。
create or replace function get_workyear (v_id in emp.empno%type) return varchar2 IS v_workyear integer; BEGIN select to_char(sysdate,'yyyy')-to_char(hiredate,'yyyy') --兩個數字字串相減的值存到整數型變數中 into v_workyear from emp where emp.empno = v_id; return v_workyear; EXCEPTION when no_data_found then dbms_output.put_line('此員工號不存在'); return -1; END get_workyear;
2.
①建表myemp。該表內容與emp一致;
②建儲存過程。儲存過程要的引數,和表裡的欄位一一對應。比如,表裡有empno,儲存過程就要有一個引數對應這欄位i_empno,型別肯定和empno一樣,如果你知道型別是number(4),就直接寫成(i_empno in number(4),...)以此類推.
③功能實現,根據empno判斷,如果myemp表裡已經有這個empno,你就根據你傳入的資訊把empno的資訊更新了,如果沒有,就把你這些傳入的欄位,插入到表裡,
eg:我只用兩個欄位來說明:empno、sal
入參1:123,1000,經過��斷,myemp表裡沒有123這個empno,那麼執行完儲存過程,這個資訊要插入到表裡;
入參2:7369,2000,經判斷,表裡已經有這個編號,但sal為800,那麼執行完儲存過程,7369的sal更新為2000;
create or replace procedure store_info (v_empnoinmyemp.empno%type, v_enameinmyemp.ename%type, v_jobinmyemp.job%type, v_mgrinmyemp.mgr%type, v_hiredateinmyemp.hiredate%type, v_salinmyemp.sal%type, v_comminmyemp.comm%type, v_deptnoinmyemp.deptno%type ) IS v_id myemp.empno%type:=0; BEGIN select count(*) into v_id from myemp where myemp.empno = v_empno; if (v_id=0) then insert into myemp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values (v_empno,v_ename,v_job,v_mgr,v_hiredate,v_sal,v_comm,v_deptno); else update myemp set myemp.ename=nvl(v_ename,myemp.ename) , myemp.job=nvl(v_job,myemp.job), myemp.mgr=nvl(v_mgr,myemp.mgr) , myemp.hiredate=nvl(v_hiredate,myemp.hiredate), myemp.sal=nvl(v_sal,myemp.sal) , myemp.comm=nvl(v_comm,myemp.comm), myemp.deptno=nvl(v_deptno,myemp.deptno) where myemp.empno = v_empno ; end if; END store_info; begin store_info(7369,null,null,null,null,2000,null,null); end;
結果

【注意】:
為什麼要把這一題關於儲存過程的題放到這裡?
因為我起初用異常處理部分來寫這一題......,這是不規範的。
本題中用到 count() 函式,count() 是用來計算滿足條件的行數的,count(*) 計算所有的行,包括空值。
用異常處理來寫本題的程式碼:
create table myemp(empno, ename, job, mgr, hiredate, sal, comm, deptno) as select empno, ename, job, mgr, hiredate, sal, comm, deptno from emp; create or replace procedure store_info (v_empnoinmyemp.empno%type, v_enameinmyemp.ename%type, v_jobinmyemp.job%type, v_mgrinmyemp.mgr%type, v_hiredateinmyemp.hiredate%type, v_salinmyemp.sal%type, v_comminmyemp.comm%type, v_deptnoinmyemp.deptno%type ) IS v_id myemp.empno%type; BEGIN select myemp.empno into v_id from myemp where myemp.empno = v_empno; update myemp set myemp.ename=v_ename, myemp.job=v_job,myemp.mgr=v_mgr, myemp.hiredate=v_hiredate, myemp.sal=v_sal, myemp.comm=v_comm,myemp.deptno=v_deptno where myemp.empno = v_id; EXCEPTION when no_data_found then insert into myemp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values (v_empno,v_ename,v_job,v_mgr,v_hiredate,v_sal,v_comm,v_deptno); END store_info;
3.
編寫PL/SQL塊,使用SELECT語句將管理者編號為空的員工的姓名及工作編號顯示出來,如果符合條件的員工多於一人,則返回字串“最高管理者人員過多!”字串,如果找到沒有符合條件的記錄,則返回字串“沒有最高管理者,請指定”
程式碼:
declare o_ename emp.ename%type; o_empno emp.empno%type; v_idemp.empno%type; begin select emp.empno into v_id from emp where emp.mgr is null; select emp.ename into o_ename from emp where emp.empno = v_id; select emp.empno into o_empno from emp where emp.empno = v_id; dbms_output.put_line('員工姓名:'||o_ename||','|| '員工編號:'||o_empno); exception when no_data_found then dbms_output.put_line('沒有最高管理者,請指定'); when too_many_rows then dbms_output.put_line('最高管理者人員過多'); end;
4.獲得每個部門的平均工資,如果平均工資大於2000,視為使用者定義的異常,提示“該部門的平均工資過高”。
declare cursor cemp is select dept.dname,avg(sal) from emp,dept where emp.deptno = dept.deptno group by emp.deptno ,dept.dname; v_dnamedept.dname%type ; v_asal emp.sal%type ; too_high_salexception; begin open cemp; loop--開啟迴圈 fetch cemp into v_dname,v_asal; exit when cemp%notfound; begin--這裡寫了一個 PL/SQL 程式碼塊,裡面可以做異常處理 if v_asal > 2000 then raise too_high_sal; end if; exception--異常處理,會終止此程式碼塊。進入下一次迴圈 when too_high_sal then dbms_output.put_line(v_dname||'該部門工資過高'); end; end loop; close cemp;--注意end loop 與 close cemp 的先後順序。必須是先結束迴圈,再關閉遊標。 end;
附一張圖:
更多Oracle相關資訊見 Oracle 專題頁面 https://www.linuxidc.com/topicnews.aspx?tid=12
Linux公社的RSS地址 : https://www.linuxidc.com/rssFeed.aspx
本文永久更新連結地址: https://www.linuxidc.com/Linux/2019-02/156844.htm