PL/SQL裏的遊標可以分爲顯式和隱式兩種,而隱式有分爲select into隱式遊標和for .. in 隱式遊標兩種。所以,我們可以認爲,有3種遊標用法: !E3o.Sc1`3_7du}
5|z ]P?Cn'S+ed
A. 顯式遊標 GgHJFM
@Cw~N S b
B. select into隱式遊標
C. for .. in 隱式遊標
如何正確的選擇使用哪種遊標,將對你的程序的性能,可讀性,代碼量等產生重大影響…… 3K5M b8Dp
1@ Oi�M2b @#V/
--By RollingPig, XxbKIO+H/dx
本文簡單的列舉了PL/SQL中用到的幾種不同類型的遊標寫法,並簡單對比了不同遊標寫法的優缺點,同時給出了一個選擇的基本原則。
j6y$q3q GpW$y ~J
本文並不包括太多的實際運行/性能測試,有興趣的話,大家可以根據示例自己測試。 g"|)x6E8M{-bF;H
1.三種遊標形式的簡單例子
A. 顯式遊標
普通顯式遊標,指的是通過定義獲得遊標,並通過open,fetch,close的等方法來操作遊標
代碼:
m.V ]di+M].O3/
declare;Jo3wJ I2dX
cursor c is select tname from tab ; A6MwD7fcG
l_tname varchar2(64);
begin +t#?'a,K7C
open c ; -CF6aI"Zp
loop
fetch c into l_tname ;E#clV7UO/R{V
exit when c%notfound ; zn2]7TK#L
dbms_output.put_line(l_tname);S)[[QV0w(`:K
end loop;,neUAgK'o-?
close c;
end;l)JM�T(h(K/~ /7j2y
/
..
..`O4q'qX6z�d3fI
:C0quz E2omSn7K
Bulk Collect的 顯式遊標
;_Z9U X|
代碼:
declare
cursor c is select tname from tab ;"_7Y a!v*k4RF
l_tname_array dbms_sql.varchar2_table;
begin
open c ; k Lg?*]&C^-t_ T
fetch c bulk collect into l_tname_array ;
for i in 1 .. l_tname_array.count loop
dbms_output.put_line(l_tname_array(i) );
end loop;
close c;sJJ,F1wc:N*Y
end;
/8o4/}m?/tSY4P#|_;s
..`QH^x
..
8J"m3m/9^!~+_NW
代碼: 3Hqf0c1V,_o
declare
cursor c is select tname from tab ;N~Q-` _7C
l_tname_array dbms_sql.varchar2_table;
begin
open c ;/Lv3m7nzc D
loopnt]'Z'R
fetch c bulk collect into l_tname_array limit 10 ;:lg9|r3Q%k:s+g5_
exit when c%notfound ;+oA9d2n}*Bf,V)^
for i in 1 .. l_tname_array.count loop
dbms_output.put_line(l_tname_array(i) );
end loop;
end loop;
close c; Ya(pl?b
end;/qN8z2v*i2|_�y
/
.. u-[ G]#CH F
..
隱式遊標相對於顯式遊標而言,指的是不需要事先Declare,也無須用open,fetch,close的等方法來操作,而是通過其它的方式來操作遊標 Y-vh;yj"IA`0di
B. select into隱式遊標 !FJ xQf/m
代碼: tRHdsjn
]*EL:L+|�c(cpS2[.U)r
declare
l_tname varchar2(100);4BS jZu'qHl
beginD&Fu`(_
select tname into l_tname from tab where rownum = 1 ;
dbms_output.put_line(l_tname);R Yz]5E,g,b
end;
/
..
..4D�XA{p0{X$LN
動態SQL 的 select into隱式遊標
#y Sx"m{K+e�u^P
代碼: %e]CU;Q;`#dU
^?eu.pq l~-{/R c @
declare
l_tname varchar2(100);c$p o)rwWDN
l_table_name varchar2(100);
l_sql varchar2(200);
beginbz(|HLl,p'[w7KH
l_table_name := 'TAB' ;2x_IQY*Z
l_sql := 'select tname from '||l_table_name ||' where rownum = 1 ' ;W0sOtY
execute immediate l_sql into l_tname;j)~:}[+/
for i in 1 .. l_tname_array.count loop)B;LO"V(Q!?
dbms_output.put_line(l_tname_array(i) );h;X_*Sm#li"{
end loop;
end;] M!tx@o n
/
..
..
代碼: D4G^bX4[.M T
declare;f3d'r/r [~uO�[
l_tname_array dbms_sql.varchar2_table;
l_table_name varchar2(100);`GeeU
l_sql varchar2(200);,A!X h)bsAC
begin
l_table_name := 'TAB' ;R#_r e3JsdY"e
l_sql := 'select tname from '||l_table_name ;;jYP%iZ?
execute immediate l_sql bulk collect into l_tname_array;
for i in 1 .. l_tname_array.count loop
dbms_output.put_line(l_tname_array(i) );
end loop;8eUg:]O[q8Pu
end;FnU`Kt
/xHR.FQ*]D#r
..{/]1Qp+mq0G
..pSKR~p&l
z%rG$M;Y
C. for .. in 隱式遊標
&R+i/k'QH+/m;nd3{
for .. in 隱式遊標通過loop的開始於結束來控制cursor的Open與Close. 8G:^R2I"Np
代碼: 8n[Xfc(Y'|
+Sd4RK}`zw$c I8x
/fFt5v^
beginR/n�R"qtfw1m
for c in (select tname from tab) loop
dbms_output.put_line(c.tname);
end loop;$t{)i?K)I)Z p,I
end; {S#p^X
/
.. ^YS:X*rQ6g(U1?
..
n.J-C&q[�jE
2.三種用法的優劣
6Eic;^6sEK9S1C
A. 顯式遊標
優點: :gS+xg I
!CDfYq'sAi$sb6A
·可以用於Bulk Collect的批量處理句式以提高性能 __'zy/S O#p~3o
7v&B-t;n'M%Y
·可以用於動態SQL的遊標處理
缺點:
V0m�N3A/}1[_4yi
·麻煩,需要定義,打開,Fetch,Close一堆代碼,增加代碼複雜度,從而增加出錯的可能性
優點
·代碼量最少
n{(R~,d^ c!j7e
·可以自動Detect 返回數據超過一行或少於一行的錯誤
7E,I%dW1xhi1~
·可以使用Bulk Collect 批量處理,但是無法使用Limit 關鍵字
缺點
·如果不使用Bulk Collect 批量處理,僅僅只能用於返回數據正好一行的情況,無法使用於返回數據超過一行或少於一行的環境 !z)?w(`seg9E
mD L+sT,r9d/
·使用Bulk Collect 批量處理時,無法使用limit 關鍵字,無法處理返回行數太多的情況(不好處理,容易造成PGA過大)
C. for .. in 隱式遊標
優點 ]oiA%j"rQ6Hq2GB8b$I#Y
1fl H7p$v'M
·代碼量遠少於顯式遊標
N$qFpQ(Y2L@
·代碼可讀性優於顯式遊標 C1MqH;x?hn)g
0dD1k/.O l[
·代碼的出錯可能性也小於顯式遊標 x[O9FtD]["H$M:G
缺點:
·無法用於動態SQL的遊標處理
X)GxI9~RB;N
·在返回行數超過10行的情況下,性能明顯不如使用Bulk Collect的顯式遊標
在性能對比方面,除非是使用了Bulk Collect,否則,三種方式沒有明顯性能差距。
[email protected]"H
·在返回數據爲一行的情況下,儘量使用select into 的隱式遊標 5_p,Y3wzd8Q*Z)u2]"Y
·返回0行或者<幾十行的情況下,使用for .. in 隱式遊標 e%J2oHf&a(T.s}
S8z(k^2W%B1Ce [}e
·或者在返回行稍多,但是不關心Fetch性能的情況下,也可考慮用for .. in 隱式遊標 O+u8dr.n _H
-Lb[@:A h{
·返回10行-100行(一個很隨意選擇的經驗值,可以自己根據情況設定),而且關心Fetch性能的情況下,可以使用select into 的隱式遊標+Bulk collect ,在獲得性能提升的情況下,代碼量也不會增加太多。 C,`j&P[~S
6kD#eJV3F;|-]
·返回行數很多,> 100, 應選用顯示遊標+Bulk collect ,以獲得較高的Fetch 性能,同時不至於使用太大的PGA內存。
"c Pt![`Q`x6N`'N9Q
·如國使用動態SQL, 則根據select list (column list) 是否固定,如果固定,仍然可以考慮使用select into 的隱式遊標+動態SQL的方式。當然,仍然需要考慮返回行數的問題。
8|F8g~*o P.}
·如果select list (column list) 不固定,則只好使用顯式遊標
·或者動態語句返回行數太多,必須用limit,那麼也只好用顯式遊標了。