.comment-link {margin-left:.6em;}

Monday, March 12, 2007

Bulk Collect

Meu mantra para performance é:
  1. SQL Puro - Me esforço o máximo para usar SQL Puro.
  2. PL/SQL - Quando a lógica é complexa.
  3. External C procs - Para ser sincero, faz algum tempo que não utilizo.
Gostaria de comentar sobre a opção dois! Escrever código PL/SQL. Cuidado para não escrever código demais. Porém, não adianta, em algum momento é preciso escrever procedures, packages, functions, etc. Então melhor utilizar bem as técnicas. Neste post, vou explorar a diferença de tratamente de cursor. Quando for inevitável o uso de cursor, tente não usar o processamento linha a linha. Uma boa técnica de tratamento para os cursores é BULK COLLECT.

Vamos ao teste, que consiste em comparar o tratamento de um mesmo cursor de duas maneiras - uma, linha a linha e outra com bulk collect. Também há uma subrotina na procedure que mostra o tempo tomado em cada execução. A lógica é alterar a coluna object_name para initcap(object_name). Apenas para fixar conceito: Qual seria o melhor forma de tratar essa modificação? ...

Muito bem! Opção 1 - SQL Puro, bastaria
insert into gtt select initcap(object_name) from t;
Entretanto, estamos estudando a melhor forma de tratar um cursor. A seguir a lógica do teste.
  1. Cursor abre, le a tabela "T" linha a linha, faz a modificação (initcap) e insere na tabela "GTT".
  2. Mostra tempo de execução 'Tempo cursor normal'
  3. Cursor abre, le a tabela "T" de 100 em 100 registros e monta um set (type - array), faz a modificação (initcap) e insere os 100 registros na tabela "GTT" volta ao item 3 até completar a tabela "T".
  4. Mostra tempo de execução 'Tempo bulk collect'
  5. Fim.
ops$marcio:LX10G> create table t as select * from all_objects;

Table created.

ops$marcio:LX10G> create global temporary table gtt on commit preserve rows
2 as select * from t where 1=0;

Table created.

ops$marcio:LX10G>
ops$marcio:LX10G> create or replace procedure p
2 is
3 cursor c is select * from t;
4 type rec is table of c%rowtype index by binary_integer;
5 l_rec rec;
6 l_time number default dbms_utility.get_time;
7 procedure show_time (p_message in varchar2) is
8 begin
9 dbms_output.put_line(p_message ||
10 trunc((dbms_utility.get_time - l_time)/100,2) || ' segundos!');
11 l_time := dbms_utility.get_time;
12 end;
13 begin
14 execute immediate ('truncate table gtt');
15 for x in c
16 loop
17 x.object_name := initcap(x.object_name);
18 insert into gtt values x;
19 end loop;
20 show_time('Tempo cursor normal ');
21 execute immediate ('truncate table gtt');
22 open c;
23 loop
24 fetch c bulk collect into l_rec limit 100;
25
26 for i in 1 .. l_rec.count
27 loop
28 l_rec(i).object_name := initcap(l_rec(i).object_name);
29 end loop;
30
31 forall i in 1 .. l_rec.count
32 insert into gtt values l_rec(i);
33
34 exit when c%notfound;
35 end loop;
36 close c;
37 show_time('Tempo bulk collect ');
38 end;
39 /

Procedure created.

ops$marcio:LX10G> show error;
No errors.
ops$marcio:LX10G>
ops$marcio:LX10G> exec p;
Tempo cursor normal 3,05 segundos!
Tempo bulk collect ,45 segundos!

PL/SQL procedure successfully completed.

ops$marcio:LX10G>
O tempo é indiscutível. Aproximadamente 3 vezes mais rápido! Para cerca de 50 mil linhas.

Labels:


Comments:
Inicialmente belo post, muito interessante. Sem sombra de dúvidas o uso do bulk collect melhora muito o desempenho, no entanto ouvi em alguns lugares que ele consome muita memória, visto que cada sessão partilha de uma área comum da memória.
Pode ocorrer em algum caso de se ter problemas de memória?
 
Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?