优化pl / sql执行时间

时间:2018-01-30 13:27:58

标签: oracle optimization plsql

我有一个pl / sql程序(Oracle 11g)需要很长时间才能执行 (3小时为195.000行)。

所以我们的目标是加快速度。

代码使用bulk collectfor all据称可以加快dmls,但是对批量收集的数据进行了一些计算。这些是在经典for loop内完成的。并且这个for循环还访问一些其他表(执行选择)以执行所需的计算。我认为那部分可以减缓一切。

考虑下面的代码(它是经过修改和删除的真实代码版本 - 只是为了给你一个正在发生的事情的要点):

procedure long_runnig_task is 
         TYPE my_record is RECORD(key1  number, key2  number, key3 number , 
         key4 number, p1 number ,p2 number, place_holder1  number, 
         place_holder2  number,place_holder3  number );

         TYPE my_record_table  IS TABLE OF my_record;
         l_data my_record_table;

 cursor c is
          select key1, key2 , key3 ,key4 ,
          (select param from paramtable where param_id=1) p1,
          (select param from paramtable where param_id=2) p2,
          0 place_holder1 ,0 place_holder2, 0 place_holder3 from mytable where myflag=4;
begin
     open c;
        loop
            begin
            fetch c bulk collect into l_data limit 1000;
            savepoint mysp;
            FOR indx IN 1 .. l_data.COUNT
           loop 
              --computations per record 
              select max(amount) into myValue1 from table3 where 
              key1=l_data(indx).kay1 and 
              key2=l_data(indx).kay2 and key3=l_data(indx).kay3 and key4=l_data(indx).kay4;

              select amount into myValue2 from table4 where key1=l_data(indx).kay1 
              and 
              key2=l_data(indx).kay2 and key3=l_data(indx).kay3 and key4=l_data(indx).kay4;

              select amount into myValue3 from table5 where key1=l_data(indx).kay1 
              and 
              key2=l_data(indx).key2 and key3=l_data(indx).key3 and key4=l_data(indx).key4;

              l_data(indx).place_holder1 := myValue1;
              l_data(indx).place_holder2 := someFunction(myValue2,l_data(indx).p1);
              l_data(indx).palce_holder3 := myValue3*l_data(indx).p2;

       end loop;

  forall indx IN 1 .. l_data.COUNT 
     update table6 set v= l_data(indx).place_holder1 where  key1=l_data(indx).key1 
     and 
     key2=l_data(indx).key2 and key3=l_data(indx).key3 and key4=l_data(indx).key4;

  forall indx IN 1 .. l_data.COUNT 
     insert into table7(col1,col2,col3) values (l_data(indx).place_holder3,sysdate,l_data(indx).place_holder2/10); 

 exception when others then
            rollback to mysp;
            raise;
 end; 
exit when c%notfound;
end loop;


exception when others then
     rollback;
    raise; 
    end;

欢迎任何有关如何优化上述性能的建议。

感谢。

1 个答案:

答案 0 :(得分:3)

这可以在两个sql语句中完成:

BEGIN
  MERGE INTO table6 tgt
    USING (SELECT key1,
                  key2,
                  key3,
                  key4,
                  (SELECT MAX(amount)
                   FROM   table3 t3
                   WHERE  t3.key1 = mt.key1
                   AND    t3.key2 = mt.key2
                   AND    t3.key3 = mt.key3
                   AND    t3.key4 = mt.key4) place_holder1
           FROM   mytable mt
           WHERE  myflag = 4) src
      ON (tgt.key1 = src.key1
         AND tgt.key2 = src.key2
         AND tgt.key3 = src.key3
         AND tgt.key4 = src.key4)
  WHEN MATCHED THEN
    UPDATE SET tgt.v = src.place_holder1;

  INSERT INTO table7
    (col1,
     col2,
     col3)
  SELECT place_holder3 * mt.p2 val3,
         SYSDATE dt,
         somefunction(place_holder2, mt.p1) val2,
  FROM   (SELECT (SELECT param
                  FROM   paramtable
                  WHERE  param_id = 1) p1,
                 (SELECT param
                  FROM   paramtable
                  WHERE  param_id = 2) p2,
                 (SELECT amount
                         FROM   table4
                         WHERE  t4.key1 = mt.key1
                         AND    t4.key2 = mt.key2
                         AND    t4.key3 = mt.key3
                         AND    t4.key4 = mt.key4) place_holder2,
                        (SELECT amount
                         FROM   table5
                         WHERE  t5.key1 = mt.key1
                         AND    t5.key2 = mt.key2
                         AND    t5.key3 = mt.key3
                         AND    t5.key4 = mt.key4)/10 place_holder3
          FROM   mytable mt
          WHERE  myflag = 4);
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK;
    RAISE;
END;
/

N.B。这假设对于mytable中的每个key1,key2,key3和key4,table3,table4和table5中都有一个对应的行。通过执行标量子查询,如果这些表中不存在行,则返回null,而不是在当前代码中生成错误(no_data_found)。如果出现这种情况,您必须修改上述过程以产生错误。

我这样做的方法是取光标sql然后将后续的sql语句作为标量子查询添加到游标的sql中。

然后我注意到插入和更新使用了不同的值(更新使用了place_holder1值,插入了place_holder2和place_holder3值(适当修改)。然后只是将它们转换为MERGE语句来做更新和插入语句。