如何加快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循环?的主要内容,如果未能解决你的问题,请参考以下文章

如何在for循环ORACLE中获取数组中的数组值

oracle存储过程中循环for in是如何使用的

oracle存储过程中循环for in是如何使用的

Oracle / Toad 中的 For 循环

Oracle 中的光标与 FOR 循环

存储过程中的 Oracle For 循环不循环