如何加快Oracle中的for循环?
Posted
技术标签:
【中文标题】如何加快Oracle中的for循环?【英文标题】:How to speed up for loop in Oracle? 【发布时间】:2014-10-29 19:20:56 【问题描述】:我正在使用 Oracle 版本 > 10gR2。 我有一张有 2000 万条记录的表。我想在将所有记录导出到文件之前对其进行处理,但是这样做的速度很慢。
你有什么建议吗?以下是我的代码:
declare
l_temp_detail clob := null;
icount number:=0;
cursor c_test is
select /*+ PARALLEL(12) */ *
from test_table t;
TYPE t_test IS TABLE OF test_table%ROWTYPE INDEX BY PLS_INTEGER;
l_test_items t_test;
BEGIN
l_temp_detail := '';
OPEN c_test;
LOOP
FETCH c_test
BULK COLLECT INTO l_test_items LIMIT 25000;
EXIT WHEN l_test_items.COUNT = 0;
FOR i in l_test_items.first .. l_test_items.last LOOP
icount := icount+1;
--doing business here
l_temp_detail := ....
END LOOP;
dbms_output.put_line(to_char(icount));
END LOOP;
CLOSE c_test;
END;
【问题讨论】:
你在做什么来“处理”数据?您确定不能在 SQL 中进行该处理吗?如果您要编写单线程代码来处理结果,则并行提示似乎毫无意义。根据 Oracle 版本,您可能能够使用dbms_parallel_execute
并行处理,但如果不知道您的处理究竟由什么组成,就很难知道。如果您的处理涉及某种 DML,FORALL
循环将比FOR
更有效。
慢了多少?批量收集还不错,正如贾斯汀所说,你当然可以用 FORALL 来改进它。看到这个:oracle.com/technetwork/issue-archive/2012/12-sep/…
因此您正在读取 2000 万行并将结果放入 CLOB,然后将结果写入文件。您是否在写入文件之前将所有 2000 万行数据放入 CLOB?我想知道减速在哪里。如果您的问题中的查询代表真正正在做的事情,那么查询并没有做太多,所以减速必须在其他地方。您是否尝试过使用分析器来确定代码在哪里花费时间?我认为这可能是一个很好的下一步。祝你好运。
您没有在实际代码中使用 dbms_output.put_line 是吗?如果是这样,那可能是您的问题(它试图假脱机到控制台)。使用UTL_FILE 在pl/sql 中写出你的数据。您还应该定期(每 10,000 行或其他)写入日志表,以便知道您在此过程中所处的位置。对于example
个人资料!轮廓 !个人资料!
【参考方案1】:
几件事。仅使用您需要的 test_table
中的那些列。根据您的需要,您似乎只需要来自test_table
的几列和有限的记录集。您不会将 2000 万条记录连接在一起。
此外,由于表很大,您可能需要检查是否有任何分区和索引。使用特定的partition
和正确的index
将提高查询执行时间。
【讨论】:
【参考方案2】:Parallel table functions 启用并行查询和并行程序处理。
示例表和数据。
drop table test_table;
create table test_table(a number);
insert into test_table select level from dual connect by level <= 1000000;
commit;
返回类型(即使你不关心结果也是必需的)
create or replace type number_nt is table of number;
并行流水线函数
--The syntax is very picky if you want to enable parallelism.
create or replace function f(p_cursor in sys_refcursor) return number_nt
pipelined parallel_enable(partition p_cursor by any) is
l_temp_detail clob := null;
icount number:=0;
type t_test is table of test_table%rowtype index by pls_integer;
l_test_items t_test;
begin
LOOP
FETCH p_cursor
BULK COLLECT INTO l_test_items LIMIT 25000;
EXIT WHEN l_test_items.COUNT = 0;
FOR i in l_test_items.first .. l_test_items.last LOOP
icount := icount+1;
--doing business here
--l_temp_detail := ....
END LOOP;
dbms_output.put_line(to_char(icount));
END LOOP;
CLOSE p_cursor;
pipe row (icount);
end;
/
查询和结果
--You may want to SUM() to get a total count.
--These aren't the results you care about, but they help show the distribution.
select column_value
from table(f(cursor(select /*+ parallel(12) */ * from test_table)));
COLUMN_VALUE
------------
124080
116160
65340
58080
79860
65340
60060
79860
59400
46960
116160
128700
【讨论】:
【参考方案3】:我还没有在 10g 上实际测试过,但是在 Oracle9 上处理 plsql 表中的大量记录比处理单个值的表效率低 10 倍。
所以 - 如果您将代码从“记录表”重构为“表记录”,您可能会看到显着的改进。您实际上不需要将各个表捆绑在一个记录中 - 我只是为了清楚起见而喜欢它。
例如
declare
type tName is table of mytable.name%type index by pls_integer;
type tAddress is table of mytable.address%type index by pls_integer;
type tPerson is record (
name tName;
address tAddress;
);
person tPerson;
begin
open somecursor;
loop
fetch somecursor bulk collect into person.name, person.address limit 25000;
--
-- processeing
--
exit when ...
end loop;
close somecursor;
end;
我不能保证这肯定会解决你的问题——这取决于你在“处理”中对数据的实际处理——例如在 plsql 中花费了多少时间以及在 sql 中花费了多少时间做 dml。
【讨论】:
以上是关于如何加快Oracle中的for循环?的主要内容,如果未能解决你的问题,请参考以下文章