除非迫不得已不要用游标

        不管是sql server 还是 oracle都提供了游标,其一般用途就是从查询结构中遍历数据。游标很好用,许多初学oracle或者sql server的程序员都喜欢用,然而如果滥用游标的话,对程序的性能会造成很大的影响。我认为:除非不得已,不要使用游标。下面是我的一次优化经历。
问题:
        在我们的系统中,需要管理产品模型,对PDM了解的人知道,其实产品模型可以当作一个特殊的零部件来处理,所以在数据表的时候产品模型与零部件使用相同的数据表来存放数据,只不过是用一个flag来标示是零部件还是产品模型。产品模型具有版本。现在的要求的是查出最新的所有的零部件信息,查询的时候允许用户自定义查询条件。
        用户自定义查询条件是从界面上通过各种选项来构造的,因此传递的是动态SQL语句,为了提高效率数据库上建立了一个存储过程专门来查询产品模型。下面是一个初学oracle不久的编程人员编写的存储过程:

 1 None.gif PROCEDURE  GETPRODMODELBYSQL(p_sql         IN   VARCHAR2 ,                 
 2 None.gif                               cur_mi_prodmodel OUT SYS_BASE.BASECURSOR)  AS
 3 None.gif   sqlstr  varchar2 ( 1000 );
 4 None.gif   idstr   varchar2 ( 1000 );
 5 None.gif   
 6 None.gif   
 7 None.gif   dis_id  number ;
 8 None.gif   maxver  number ;
 9 None.gif         cursor  disid_curs  is
10 None.gif               select   distinct  pt_id
11 None.gif               from  mi_part
12 None.gif               where  pt_flag  =   2 ;                               
13 None.gif    BEGIN
14 None.gif     open  disid_curs;
15 None.gif         loop
16 None.gif              fetch  disid_curs
17 None.gif              into  dis_id;
18 None.gif              exit   when  disid_curs %  notfound;
19 None.gif             
20 None.gif              select   max (pv_id)
21 None.gif              into  maxver
22 None.gif              from  mi_partver
23 None.gif              where  pv_partid  =  dis_id;
24 None.gif             
25 None.gif              if  maxver  is   null   then
26 None.gif                 idstr : =  idstr;
27 None.gif              else
28 None.gif                 idstr : =  idstr  ||  maxver  ||   ' , ' ;
29 None.gif              end   if ;
30 None.gif           end  loop;
31 None.gif
32 None.gif          idstr  : =   rtrim (idstr,  ' , ' );
33 None.gif          idstr  : =   ltrim (idstr,  ' , ' );
34 None.gif           if  idstr  is   null   then
35 None.gif              sqlstr : =  p_sql  ||   '  ORDER BY PV_PRODUCTID,PT_PARTNUMBER ' ;
36 None.gif           else
37 None.gif              sqlstr : =  p_sql  ||   '  and MI_PARTVER.pv_id in ( '   ||  idstr  ||   ' )  ORDER BY PV_PRODUCTID,PT_PARTNUMBER ' ;
38 None.gif            end   if ;
39 None.gif
40 None.gif         open  cur_mi_prodmodel  for  sqlstr;
41 None.gif    END  GETPRODMODELBYSQL;

这个存储过程可以正常工作,但是其速度执行起来非常慢,如果数据表中有很多数据的话,其速度很可能是直线下降,究其原因是因为使用游标来遍历然后构造查询语句。对这个存储过程进行优化:
 1 None.gif PROCEDURE  GETPRODMODELBYSQL(p_sql         IN   VARCHAR2 ,                 
 2 None.gif                               cur_mi_prodmodel OUT SYS_BASE.BASECURSOR)  AS
 3 None.gif   s  varchar2 ( 300 );
 4 None.gif   sqlstr  varchar2 ( 1000 );
 5 ExpandedBlockStart.gifContractedBlock.gif    /**/ /*idstr  varchar2(1000);
 6InBlock.gif   
 7InBlock.gif   
 8InBlock.gif   dis_id number;
 9InBlock.gif   maxver number;
10InBlock.gif        cursor disid_curs is
11InBlock.gif              select distinct pt_id
12InBlock.gif              from mi_part
13ExpandedBlockEnd.gif              where pt_flag = 2;  */
                             
14 None.gif    BEGIN
15 ExpandedBlockStart.gifContractedBlock.gif     /**/ /*open disid_curs;
16InBlock.gif         loop
17InBlock.gif             fetch disid_curs
18InBlock.gif             into dis_id;
19InBlock.gif             exit when disid_curs% notfound;
20InBlock.gif             
21InBlock.gif             select max(pv_id)
22InBlock.gif             into maxver
23InBlock.gif             from mi_partver
24InBlock.gif             where pv_partid = dis_id;
25InBlock.gif             
26InBlock.gif             if maxver is null then
27InBlock.gif                 idstr := idstr;
28InBlock.gif             else
29InBlock.gif                 idstr := idstr || maxver || ',';
30InBlock.gif             end if;
31ExpandedBlockEnd.gif          end loop;*/

32 None.gif          s: = '  and MI_PARTVER.pv_id in (select a.pv_id from mi_partver a,mi_part c where a.pv_partid = c.pt_id and c.pt_flag =2 and
33 None.gifa.pv_id in (select e.pv_id from mi_partver e
34 None.gifwhere e.pv_id in (select max(b.pv_id) from mi_partver b where e.pv_partid = b.pv_partid))) ' ;
35 ExpandedBlockStart.gifContractedBlock.gif           /**/ /*idstr  := rtrim(idstr, ',');
36InBlock.gif          idstr  := ltrim(idstr, ',');
37InBlock.gif          if idstr is null then
38InBlock.gif              sqlstr := p_sql || ' ORDER BY PV_PRODUCTID,PT_PARTNUMBER';
39InBlock.gif          else
40InBlock.gif              sqlstr := p_sql || ' and MI_PARTVER.pv_id in (' || idstr || ')  ORDER BY PV_PRODUCTID,PT_PARTNUMBER';
41ExpandedBlockEnd.gif           end if;*/

42 None.gif              sqlstr : =  p_sql  ||  s  ||    ' ORDER BY PV_PRODUCTID,PT_PARTNUMBER ' ;
43 None.gif         open  cur_mi_prodmodel  for  sqlstr;
44 None.gif    END  GETPRODMODELBYSQL;

这里的有效语句是使用查询语句来构造查询,其结果是速度得到了数量级的提高,而且避免了一个隐患就是缓冲区溢出,因为原来的存储过程中定义了几个varchar2(1000)的变量,实际上这个变量的上界是不可以预期的,而缓冲区溢出也是很多初学oracle的程序员经常忽略甚至想不到的问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值