PL/SQL 中如何正确选择游标类型
PL/SQL里的游标可以分为显式和隐式两种,而隐式有分为select into隐式游标和for .. in 隐式游标两种。所以,我们可以认为,有3种游标用法: !E3o.Sc1`3_7du}
5|z ] P?Cn'S+ed
A. 显式游标 GgH JFM
@Cw~N S b
B. select into隐式游标
C. for .. in 隐式游标
如何正确的选择使用哪种游标,将对你的程序的性能,可读性,代码量等产生重大影响…… 3K5M b8Dp
1@ Oi�M2b @#V/
--By RollingPig, Xxb KIO+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
..
PL/SQL里的游标可以分为显式和隐式两种,而隐式有分为select into隐式游标和for .. in 隐式游标两种。所以,我们可以认为,有3种游标用法: !E3o.Sc1`3_7du}
5|z ] P?Cn'S+ed
A. 显式游标 GgH JFM
@Cw~N S b
B. select into隐式游标
C. for .. in 隐式游标
如何正确的选择使用哪种游标,将对你的程序的性能,可读性,代码量等产生重大影响…… 3K5M b8Dp
1@ Oi�M2b @#V/
--By RollingPig, Xxb KIO+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
..
2006-9-27 12:43
小熊
Bulk Collect的 显式游标 + limit
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
loop nt]'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
begin D&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;`#d U
^?eu.pq l~-{/R c @
declare
l_tname varchar2(100); c$p o)rwWDN
l_table_name varchar2(100);
l_sql varchar2(200);
begin bz(|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
/
..
..
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
loop nt]'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
begin D&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;`#d U
^?eu.pq l~-{/R c @
declare
l_tname varchar2(100); c$p o)rwWDN
l_table_name varchar2(100);
l_sql varchar2(200);
begin bz(|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
/
..
..
2006-9-27 12:45
小熊
动态SQL 的 select into隐式游标 + Bulk Collect
"b,tT&q?W /P
代码: 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^
begin R/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一堆代码,增加代码复杂度,从而增加出错的可能性
代码: 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^
begin R/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一堆代码,增加代码复杂度,从而增加出错的可能性
2006-9-27 12:45
小熊
B. select into隐式游标B
优点
·代码量最少
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,否则,三种方式没有明显性能差距。
优点
·代码量最少
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,否则,三种方式没有明显性能差距。
2006-9-27 12:46
小熊
3.具体的选择
b yTQ1R"F2|
vO0U@.J"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 P t![`Q`x6N`'N9Q
·如国使用动态SQL, 则根据select list (column list) 是否固定,如果固定,仍然可以考虑使用select into 的隐式游标+动态SQL的方式。当然,仍然需要考虑返回行数的问题。
8|F8g~*o P.}
·如果select list (column list) 不固定,则只好使用显式游标
·或者动态语句返回行数太多,必须用limit,那么也只好用显式游标了。
vO0U@.J"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 P t![`Q`x6N`'N9Q
·如国使用动态SQL, 则根据select list (column list) 是否固定,如果固定,仍然可以考虑使用select into 的隐式游标+动态SQL的方式。当然,仍然需要考虑返回行数的问题。
8|F8g~*o P.}
·如果select list (column list) 不固定,则只好使用显式游标
·或者动态语句返回行数太多,必须用limit,那么也只好用显式游标了。