pl/sql中三种游标循环效率对比
pl/sql中三种游标循环效率对比
这里主要对比以下三种格式的游标循环:
1.单条处理 open 游标; LOOP FETCH 游标 INTO 变量; EXIT WHEN 条件; END LOOP; CLOSE 游标; 2.批量处理 open 游标; FETCH 游标 BULK COLLECT INTO 集合变量; CLOSE 游标; 3.隐式游标 for x in (sql语句) loop ...--逻辑处理 end loop;
以上为工作中常见的几种游标处理方式,一般来说批量处理的速度要最好,隐式游标的次之,单条处理的最差,但是在我的实际工作中发现大部分使用的还是第一种游标处理。
归其原因竟是对集合变量及批量处理的效率等问题不了解所致。
这里简单的测试一下以上三种游标的效率,并分析trace文件来查看这3种处理方式的本质。
--创建测试大表 [sql] 00:09:54 SCOTT@orcl> create table big_data as select 'Cc'||mod(level,8) a,'Dd'|| mod(level,13) b from dual connect by level<1000000; Table created. Elapsed: 00:00:05.87 00:11:17 SCOTT@orcl> select count(*) from big_data; COUNT(*) ---------- 999999 1 row selected. Elapsed: 00:00:00.07 --分别执行以上三种方式的游标处理的plsql块 [sql] 00:11:21 SCOTT@orcl> declare 00:17:54 2 cursor c_a is 00:17:54 3 select a from big_data; 00:17:54 4 00:17:54 5 v_a big_data.a%type; 00:17:54 6 begin 00:17:54 7 open c_a; 00:17:54 8 loop 00:17:54 9 fetch c_a into v_a; 00:17:54 10 exit when c_a%notfound; 00:17:54 11 end loop; 00:17:54 12 close c_a; 00:17:54 13 end; 00:17:56 14 / PL/SQL procedure successfully completed. Elapsed: 00:00:07.42 00:18:05 SCOTT@orcl> declare 00:19:56 2 cursor c_a is 00:19:56 3 select a from big_data; 00:19:56 4 00:19:56 5 type t_a is table of c_a%rowtype; 00:19:56 6 v_a t_a; 00:19:56 7 begin 00:19:56 8 open c_a; 00:19:56 9 --批量处理 00:19:56 10 fetch c_a bulk collect into v_a; 00:19:56 11 close c_a; 00:19:56 12 end; 00:19:57 13 / PL/SQL procedure successfully completed. Elapsed: 00:00:00.64 00:22:55 SCOTT@orcl> declare 00:23:18 2 v_a big_data.a%type; 00:23:18 3 begin 00:23:18 4 --批量处理 00:23:18 5 for x in (select a from big_data) loop 00:23:18 6 v_a:=x.a; 00:23:18 7 end loop; 00:23:18 8 end; 00:23:18 9 / PL/SQL procedure successfully completed. Elapsed: 00:00:00.79
注意对比消耗时间,1为7.42s, 2为0.64s, 3为0.79s
在执行pl/sql块之前,需要执行语句: alter session set sql_trace=true;
以便之后查看trace文件.
第一个游标方式的trace文件如下:(单条处理)
PARSING IN CURSOR #7 len=181 dep=0 uid=84 oct=47 lid=84 tim=1357453194221500 hv=4093379502 ad='3ab9f6ec' sqlid='3nz96vvtzs0xf' declare cursor c_a is select a from big_data; v_a big_data.a%type; begin open c_a; loop fetch c_a into v_a; exit when c_a%notfound; end loop; close c_a; end; END OF STMT PARSE #7:c=7998,e=8406,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=0,tim=1357453194221495 ===================== PARSING IN CURSOR #4 len=444 dep=2 uid=84 oct=3 lid=84 tim=1357453194225811 hv=1611503607 ad='3ab64c10' sqlid='c7tu1h9h0v5zr' SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),:"SYS_B_0"), NVL(SUM(C2),:"SYS_B_1") FROM (SELECT /*+ NO_PARALLEL("BIG_DATA") FULL("BIG_DATA") NO_PARALLEL_INDEX("BIG_DATA") */ :"SYS_B_2" AS C1, :"SYS_B_3" AS C2 FROM "BIG_DATA" SAMPLE BLOCK (:"SYS_B_4" , :"SYS_B_5") SEED (:"SYS_B_6") "BIG_DATA") SAMPLESUB END OF STMT PARSE #4:c=2000,e=1958,p=0,cr=0,cu=0,mis=1,r=0,dep=2,og=1,plh=0,tim=1357453194225807 *** 2013-01-06 14:19:54.284 EXEC #4:c=3998,e=58289,p=0,cr=0,cu=0,mis=1,r=0,dep=2,og=1,plh=3098652591,tim=1357453194284371 FETCH #4:c=18997,e=19074,p=0,cr=55,cu=0,mis=0,r=1,dep=2,og=1,plh=3098652591,tim=1357453194303593 STAT #4 id=1 cnt=1 pid=0 pos=1 obj=0 op='SORT AGGREGATE (cr=55 pr=0 pw=0 time=0 us)' STAT #4 id=2 cnt=27300 pid=1 pos=1 obj=75053 op='TABLE ACCESS SAMPLE BIG_DATA (cr=55 pr=0 pw=0 time=130371 us cost=19 size=61752 card=5146)' CLOSE #4:c=0,e=86,dep=2,type=0,tim=1357453194318217 ===================== PARSING IN CURSOR #6 len=22 dep=1 uid=84 oct=3 lid=84 tim=1357453194318768 hv=3992159408 ad='3aae4de0' sqlid='3w21sgzqz715h' SELECT A FROM BIG_DATA END OF STMT PARSE #6:c=28995,e=96556,p=0,cr=56,cu=0,mis=1,r=0,dep=1,og=1,plh=3104650627,tim=1357453194318766 EXEC #6:c=0,e=31,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=3104650627,tim=1357453194318875 FETCH #6:c=0,e=405,p=20,cr=4,cu=0,mis=0,r=1,dep=1,og=1,plh=3104650627,tim=1357453194319360 FETCH #6:c=0,e=13,p=0,cr=1,cu=0,mis=0,r=1,dep=1,og=1,plh=3104650627,tim=1357453194319425 FETCH #6:c=0,e=6,p=0,cr=1,cu=0,mis=0,r=1,dep=1,og=1,plh=3104650627,tim=1357453194319463 FETCH #6:c=0,e=5,p=0,cr=1,cu=0,mis=0,r=1,dep=1,og=1,plh=3104650627,tim=1357453194319496 FETCH #6:c=0,e=7,p=0,cr=1,cu=0,mis=0,r=1,dep=1,og=1,plh=3104650627,tim=1357453194319531 FETCH #6:c=0,e=5,p=0,cr=1,cu=0,mis=0,r=1,dep=1,og=1,plh=3104650627,tim=1357453194319564 ... 1000108 FETCH #6:c=0,e=47,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=3104650627,tim=1357453214142218 1000109 STAT #6 id=1 cnt=999999 pid=0 pos=1 obj=75053 op='TABLE ACCESS FULL BIG_DATA (cr=1000002 pr=1832 pw=0 time=2281997 us cost=512 size=18637659 card=810333)' 1000110 CLOSE #6:c=0,e=1,dep=1,type=3,tim=1357453214142317 1000111 EXEC #7:c=19290067,e=19920346,p=1832,cr=1000058,cu=0,mis=0,r=1,dep=0,og=1,plh=0,tim=1357453214142338 1000112 =====================
其中SELECT /* OPT_DYN_SAMP */这个大sql为CBO的动态采样SQL.这里也耗费了一些CPU time(即c的值).
我们发现大概有100多万的FETCH语句在trace中,也就是一条条的处理的,最终耗费的cpu time高达19290067,显然这种游标处理的效率是极其低下的.(尤其很多开发人员还喜欢对此类游标加锁后,单条处理,效率之低,不敢想象)
第二个游标方式的trace文件如下:(批量处理)
PARSING IN CURSOR #5 len=182 dep=0 uid=84 oct=47 lid=84 tim=1357454222243170 hv=3525186369 ad='3aa08740' sqlid='fr3sb9r91w4u1' declare cursor c_a is select a from big_data; type t_a is table of c_a%rowtype; v_a t_a; begin open c_a; --?úá?′|àí fetch c_a bulk collect into v_a; close c_a; end; END OF STMT PARSE #5:c=47993,e=48253,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,plh=0,tim=1357454222243163 ===================== PARSING IN CURSOR #7 len=22 dep=1 uid=84 oct=3 lid=84 tim=1357454222243720 hv=3992159408 ad='3aae4de0' sqlid='3w21sgzqz715h' SELECT A FROM BIG_DATA END OF STMT PARSE #7:c=0,e=59,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=3104650627,tim=1357454222243719 EXEC #7:c=1000,e=61,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=1,plh=3104650627,tim=1357454222243839 *** 2013-01-06 14:37:02.816 FETCH #7:c=572913,e=572454,p=1832,cr=1835,cu=0,mis=0,r=999999,dep=1,og=1,plh=3104650627,tim=1357454222816387 STAT #7 id=1 cnt=999999 pid=0 pos=1 obj=75053 op='TABLE ACCESS FULL BIG_DATA (cr=1835 pr=1832 pw=0 time=633174 us cost=512 size=18637659 card=810333)' CLOSE #7:c=0,e=2,dep=1,type=3,tim=1357454222816543 EXEC #5:c=586911,e=586709,p=1832,cr=1835,cu=0,mis=0,r=1,dep=0,og=1,plh=0,tim=1357454222830293
其中的乱码为注释的中文字符.
使用BULK COLLECT 批量处理的方式,显然要快了许多.我们可以看到,它是先执行游标语句SELECT A FROM BIG_DATA,然后一次FETCH出来.一次处理999999行.
第三个游标方式的trace文件如下
上一个:oracle多行合并技巧
下一个:ASM上的备份集如何转移到文件系统中
- 更多SQLServer疑问解答:
- 配置MSSQL复制指定快照文件夹提示:不是有效的路径或文件名
- 详细解读varchar和Nvarchar区别
- SQL SERVER 2005 同步复制技术
- 进程未能大容量复制到表 解决方法
- MSSql实例教程:MSSql数据库同步
- SQLServer2000同步复制技术实现(分发和订阅)
- sqlserver
- SQLSERVER
- 测试 sqlserver 最大用户数连接
- 写出一条Sql语句:取出表A中第31到第40记录(SQLServer,以自动增长的ID作为主键,注意:ID可能不是连续的
- sqlserver2008的安装问题。
- 为什么安装的SQL server 2008我的没有MSSQLSERVER协议啊?
- 怎样把exel表里的数据复制到sqlserver表里
- sqlserver数据库主键和外键问题
- sqlserver 中的左表连接查询和右表连接查询有啥不同?有什么用?