我有一个pl / sql程序(Oracle 11g)需要很长时间才能执行 (3小时为195.000行)。
所以我们的目标是加快速度。
代码使用bulk collect
和for 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;
欢迎任何有关如何优化上述性能的建议。
感谢。
答案 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语句来做更新和插入语句。