PLSQL开发应该尽量避免使用游标,乱用游标会引起严重性能问题
最近几年做过很多ORACLE EBS优化项目,除了乱写SQL导致性能问题之外,影响性能最大的就是乱用游标
如果是INSERT,直接INSERT INTO SELECT,或者INSERT /*+ PARALLEL(6) ENABLE_PARALLEL_DML */ SELECT...,不要用游标LOOP循环单行INSERT
如果是UPDATE,DELETE,为了避免大事务,可以用ROWID+批量游标,根据数据集大小1-10W行提交一次
如果是要做复杂的计算,可以用临时表存放SQL多表关联的中间结果集,不要游标套游标来做复杂计算
数据量少(10w行内),如果没有游标套游标,如果游标中没有复杂慢SQL,如果游标中没有胡乱调用复杂自定义函数,可以适当使用游标
数据量很少,爱咋咋用
虽然我们嘴上说要尽量避免使用游标,但是有时候很多PLSQL代码都已经写了10-20年了,里面大量使用了游标,木已成舟,不可能推翻重来了
现在来测试Oracle-PG-DM-崖山-openGauss游标LOOP性能,看看国产库游标LOOP性能和Oracle差距有多大
如果国产库游标LOOP性能和Oracle差距不大,就加测游标LOOP+INSERT,批量游标,如果性能差距太大就不测了,免得丢人
TEST01数据来自DBA_OBJECTS,重复512次,共4500W行,不创建索引
测试PLSQL代码如下
begin
for cur in (select object_id from test01) loop
null; ---无计算,测试纯LOOP性能
end loop;
end;
/
declare
v_cnt number := 0;
begin
for cur in (select object_id from test01) loop
v_cnt:=v_cnt+cur.object_id; ---带计算,测试LOOP循环计算性能
end loop;
dbms_output.put_line(v_cnt);
end;
/
开始测试(多次运行,确保TEST01被CACHE在内存中)
Oracle11.2.0.4
SQL> begin
for cur in (select object_id from test01) loop
null;
end loop;
end;
/ 2 3 4 5 6
PL/SQL procedure successfully completed.
Elapsed: 00:00:07.56
SQL> declare
v_cnt number := 0;
begin
for cur in (select object_id from test01) loop
v_cnt:=v_cnt+cur.object_id;
end loop;
dbms_output.put_line(v_cnt);
end;
/ 2 3 4 5 6 7 8 9
1953663546368
PL/SQL procedure successfully completed.
Elapsed: 00:00:08.85
PG18
postgres=# do $$
postgres$# declare
postgres$# cur record;
postgres$# begin
postgres$# for cur in select object_id from test01
postgres$# loop
postgres$# null;
postgres$# end loop;
postgres$# end $$;
DO
Time: 9259.170 ms (00:09.259)
postgres=# do $$
postgres$# declare
postgres$# v_cnt numeric := 0;
postgres$# cur record;
postgres$# begin
postgres$# for cur in select object_id from test01
postgres$# loop
postgres$# v_cnt := v_cnt + cur.object_id;
postgres$# end loop;
postgres$# raise notice 'Result: %', v_cnt;
postgres$# end $$;
NOTICE: Result: 1953663546368
DO
Time: 15820.366 ms (00:15.820)
DM8
SQL> begin
for cur in (select object_id from test01) loop
null;
end loop;
end;
/
2 3 4 5 6
DMSQL executed successfully
used time: 00:00:17.230. Execute id is 604.
SQL> declare
v_cnt number := 0;
begin
for cur in (select object_id from test01) loop
v_cnt:=v_cnt+cur.object_id;
end loop;
dbms_output.put_line(v_cnt);
end;
/2 3 4 5 6 7 8 9
DMSQL executed successfully
used time: 00:00:21.558. Execute id is 605.
崖山23.5.1
SQL> begin
for cur in (select object_id from test01) loop
null;
end loop;
end;
/ 2 3 4 5 6
PL/SQL Succeed.
Elapsed: 00:00:17.487
SQL> declare
v_cnt number := 0;
begin
for cur in (select object_id from test01) loop
v_cnt:=v_cnt+cur.object_id;
end loop;
dbms_output.put_line(v_cnt);
end;
/ 2 3 4 5 6 7 8 9
PL/SQL Succeed.
Elapsed: 00:00:24.163
openGauss7.0
openGauss=# begin
openGauss$# for cur in (select object_id from test01) loop
openGauss$# null;
openGauss$# end loop;
openGauss$# end;
openGauss$# /
ANONYMOUS BLOCK EXECUTE
Time: 10391.383 ms
openGauss=# declare
openGauss-# v_cnt number := 0;
openGauss-# begin
openGauss$# for cur in (select object_id from test01) loop
openGauss$# v_cnt:=v_cnt+cur.object_id;
openGauss$# end loop;
openGauss$# RAISE NOTICE 'Result: %', v_cnt;
openGauss$# end;
openGauss$# /
NOTICE: Result: 1953663546368
ANONYMOUS BLOCK EXECUTE
Time: 26544.038 ms
测试结果汇总如下图所示:

结论:
Oracle游标LOOP循环性能最好,游标LOOP带计算性能损失1.29秒
PG游标LOOP循环性能很好,游标LOOP带计算性性能损失6.56秒
DM游标LOOP循环性能不好,游标LOOP带计算性能损失4.32秒
崖山游标LOOP循环性能不好,游标LOOP带计算性能损失6.67秒
openGauss游标LOOP性能很好,游标LOOP带计算性能差16.15秒
国产库在游标处理的性能上还有很大性能提升空间