oracle存储过程的简单学习2
1.選用何種游標?
顯示游標分為:普通游標,參數化游標和游標變量三種。
create or replace procedure proc(p varchar2)
as
v_rownum number(10) := 1;
cursor c1 is select ename from emp where rownum = 1;
cursor c2 is select ename from emp where rownum = v_rownum;
cursor c3(p_rownum number) is select ename from emp where rownum = p_rownum;
type c_c is ref cursor;
c4 c_c;
v1 varchar2(20);
begin
?open c1;
?fetch c1 into v1;
?dbms_output.put_line('1.' || v1);
?close c1;
?open c2;
?fetch c2 into v1;
?dbms_output.put_line('2.' || v1);
?close c2;
?open c3(1);
?fetch c3 into v1;
?dbms_output.put_line('3.' || v1);
?close c3;
?open c4?for?select ename from emp where rownum = 1;
?fetch c4 into v1;
?dbms_output.put_line('4.' || v1);
?close c4;
end; ?
-- 調用
call ? proc(1);
-- 說明
cursor c1 is select ename from emp where rownum = 1;
這一句是定義了一個最普通的游標,把整個查詢已經寫死,調用時不可以作任何改變。
cursor c2 is select ename from emp where rownum = v_rownum;
這一句并沒有寫死,查詢參數由變量v_rownum來決定。需要注意的是v_rownum必須在這個游標定義之前聲明。
cursor c3(p_rownum number) is select ename from emp where rownum = p_rownum;
這一條語句與第二條作用相似,都是可以為游標實現動態的查詢。但是它進一步的縮小了參數的作用域范圍。但是可讀性降低了不少。
type c_c is ref cursor;
c4 c_c;
先定義了一個引用游標類型,然后再聲明了一個游標變量。
open c4 for select ename from emp where rownum = 1;
然后再用open for 來打開一個查詢。需要注意的是它可以多次使用,用來打開不同的查詢。
從動態性來說,游標變量是最好用的,但是閱讀性也是最差的。
注意,游標的定義只能用使關鍵字IS,它與AS不通用。
2.游標的循環策略
create or replace procedure proc1
as
cursor c1 is select ename,sal from emp ;
v1 varchar2(20);
v2 number(4);
begin
?open c1;
?if?c1%found?= true then
? ?dbms_output.put_line('found true ...');
?elsif?c1%found?= false then
? ?dbms_output.put_line('found false ...');
?else
? ?dbms_output.put_line('found null ...');
?end if;
?--1.loop循環
?loop
? ?fetch c1 into v1,v2;
? ?exit when c1%notfound;
? ?dbms_output.put_line('ename: ' || v1 || ',val:' || v2);
?end loop;
?dbms_output.put_line('--- loop end ...');
?close c1;
/*exit when語句一定要緊跟在fetch之后。必避免多余的數據處理。
處理邏輯需要跟在exit when之后。這一點需要多加小心。
循環結束后要記得關閉游標*/
?--2.while循環
?open c1;
fetch c1 into v1,v2;
?while c1%found loop
? ? ?dbms_output.put_line('ename: ' || v1 || ',val:' || v2);
? ? ?fetch c1 into v1,v2;
?end loop;
?close c1; ?
?dbms_output.put_line('---while end---'); ?
?/*
我們知道了一個游標打開后,必須執行一次fetch語句,游標的屬性才會起作用。
所以使用while 循環時,就需要在循環之前進行一次fetch動作。
而且數據處理動作必須放在循環體內的fetch方法之前。
循環體內的fetch方法要放在最后。否則就會多處理一次。這一點也要非常的小心。
總之,使用while來循環處理游標是最復雜的方法。
?*/
? --3.for循環
? for v in c1 loop
? ? v1 := v.ename;
? ? v2 := v.sal;
? ? dbms_output.put_line('ename: ' || v1 || ',val:' || v2);
? end loop;
? dbms_output.put_line('---for end---'); ?
/*
可見for循環是比較簡單實用的方法。
首先,它會自動open和close游標。解決了你忘記打開或關閉游標的煩惱。
其它,自動定義了一個記錄類型及聲明該類型的變量,并自動fetch數據到這個變量中。
我們需要注意v 這個變量無需要在循環外進行聲明,無需要為其指定數據類型。
它應該是一個記錄類型,具體的結構是由游標決定的。
這個變量的作用域僅僅是在循環體內。
把v看作一個記錄變量就可以了,如果要獲得某一個值就像調用記錄一樣就可以了。
如v.ename
由此可見,for循環是用來循環游標的最好方法。高效,簡潔,安全。
但遺憾的是,常常見到的卻是第一種方法。所以從今之后得改變這個習慣了
*/
end;
--調用
call proc1();
說明:
在打開一個游標之后,馬上檢查它的%found或%notfound屬性,
它得到的結果即不是true也不是false.而是null.
必須執行一條fetch語句后,這些屬性才有值。
3.select into不可忽視的問題
<1.>
我們知道在pl/sql中要想從數據表中向變量賦值,需要使用select into 子句。
但是它會帶動來一些問題,如果查詢沒有記錄時,會拋出no_data_found異常。
如果有多條記錄時,會拋出too_many_rows異常。
這個是比較糟糕的。一旦拋出了異常,就會讓過程中斷。
特別是no_data_found這種異常,沒有嚴重到要讓程序中斷的地步,可以完全交給由程序進行處理。 ?
eg1:
create or replace procedure proc2
AS
? ? ?v varchar2(20);
begin
?dbms_output.put_line('---開始:================');
select ename into v from emp where 1 = 0;
?dbms_output.put_line('---' || v);
exception
?when no_data_found then
? ? ?dbms_output.put_line('no data found...。。。');
end;
--調用
call proc2();
<2.>
說明:加exception → 這樣做換湯不換藥,程序仍然被中斷。
? ? ?可能這樣不是我們所想要的。
select into做為一個獨立的塊,在這個塊中進行異常處理 。
? 這是一種比較好的處理方式了。不會因為這個異常而引起程序中斷。
? 如下面的例子:
eg2:
create or replace procedure proc3
as
? ? ?v varchar2(20);
begin
begin
? ?dbms_output.put_line('---begin...========');
select ename into v from emp where 1 = 0;
? ?dbms_output.put_line('---' || v);
exception
? ?when no_data_found then
? ? ?dbms_output.put_line('no data found...give new value...');
? ? ?v := '';
end; ? ?
? dbms_output.put_line('v :' || v);
end;
-- 調用:
call proc3();
<3.>使用游標:這樣就完全的避免了no_data_found異常。完全交由程序員來進行控制了。
eg3:
create or replace procedure proc4
as
? ? ? v varchar2(20);
cursor c is select ename from emp where 1=0;
begin
?open c;
? ?dbms_output.put_line('---begin...========');
fetch c into v;
? ?dbms_output.put_line('v :' || v);
?close c;
? ?dbms_output.put_line('end...v :' || v);
end;
--
call proc4(); ? ?
4. ?too_many_rows 異常的問題。 ?
Too_many_rows 這個問題比起no_data_found要復雜一些。
給一個變量賦值時,但是查詢結果有多個記錄。
處理這種問題也有兩種情況:
<1>. 多條數據是可以接受的,也就是說從結果集中隨便取一個值就行。
這種情況應該很極端了吧,如果出現這種情況,也說明了程序的嚴謹性存在問題。
<2>. 多條數據是不可以被接受的,在這種情況肯定是程序的邏輯出了問題,也說是說原來根本就不會想到它會產生多條記錄。
對于第一種情況,就必須采用游標來處理,而對于第二種情況就必須使用內部塊來處理,重新拋出異常。
多條數據可以接受,隨便取一條,這個跟no_data_found的處理方式一樣,使用游標。
我這里僅說第二種情況,不可接受多條數據,但是不要忘了處理no_data_found哦。
這就不能使用游標了,必須使用內部塊。
需要注意的是一定要加上對no_data_found的處理,對出現多條記錄的情況則繼續拋出異常,讓上一層來處理。
總之對于select into的語句需要注意這兩種情況了。需要妥當處理啊。
eg4:
create or replace procedure proc5
as
? ? ? v varchar2(20);
begin
begin
select ename into v from emp where rownum < 5;
exception
? ? ?when no_data_found then
? ? ? ?v := null;
when too_many_rows then ?
? ? ? raise_application_error('-20000','對v賦值時,找到多行記錄!');
? ?end;
? ?dbms_output.put_line(v);
end; ?
--
call proc5(); ? ?
5.在存儲過程中返回結果集
見參考文章吧:→ 【http://www.cnblogs.com/chinafine/archive/2010/07/12/1776102.html】
------------------demo---------------------
create or replace procedure proc6(v1 varchar2,v2 number)
as
? ? ? total number(4) := 0;
? ? ? cursor c is select * from empa ;-- where 1=0;
begin
?if v1is not null??and v2?!= 0?then
? ?dbms_output.put_line('ok' || v1 || v2); ? ?
?elsif v1?is null?then
? ? ?dbms_output.put_line('v1 is null'); ?
-- elsif v1 = '' then
?-- ? ?dbms_output.put_line('v1 is kong...'); ?
?elsif v2 = 0 then
? ? ?dbms_output.put_line('v2 is 0'); ? ? ?
?end if;
for varObj in c loop
total := c%Rowcount ;
? ? dbms_output.put_line(c%Rowcount || 'empno :' || varObj.empno || 'ename: ' || varObj.ename || 'sal:' || varObj.sal);
? end loop;
? dbms_output.put_line('total:' || total);
? if?total <= 0?then
? ? ?dbms_output.put_line('total:' || total);
?raise_application_error('ORA-00973','rownumber is zero!');
? end if;
end;
--
call proc6(null,1);
call proc6('',1);-- ''會被認為是null
call proc6('tom',0);
==========================2013-12-17-說明===================
1.texit用來跳出循環 ?2.return跳出存儲過程.參考地址:http://sunrise-king.iteye.com/blog/391374
http://www.jb51.net/article/34230.htm
? ? ?本文轉自韓立偉 51CTO博客,原文鏈接:http://blog.51cto.com/hanchaohan/1340791,如需轉載請自行聯系原作者
總結
以上是生活随笔為你收集整理的oracle存储过程的简单学习2的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DS4700电池更换步骤
- 下一篇: GoogleAppEngine是什么?