Pular para o conteúdo

Performance dos diferentes tipos de cursores no Oracle

Utilizando Cursores no Oracle

Olá, pessoal !

Neste artigo vamos simular a utilização dos diversos tipos de cursores no Oracle, por final vamos comparar a performance deles. Utilizaremos a extração de uma tabela com uma massa de dados de 50 mil registros para que possamos fazer um teste considerável.

01. Criar tabela

Crie a estrutura da tabela

create table TB_PESSOA
(
 IDPESSOA       INTEGER not null,
 NOME           VARCHAR2(40) not null,
 DATANASCIMENTO DATE not null,
 ALTURA         NUMBER(5,2),
 PESO           NUMBER(5,2)
)

02. Inserir registros

Vamos inserir 50 mil registros na tabela para criarmos um volume de dados considerável para nossos testes.

declare
  v_id_pessoa tb_pessoa.idpessoa%type;
  v_nome      tb_pessoa.nome%type;
  v_dt_nascimento tb_pessoa.datanascimento%type;

begin
  v_nome := 'Joao da Silva';
  v_dt_nascimento := to_date('01/01/1900');
 for dd in 1..50000 loop
   select nvl(max(p.idpessoa),0)+1
      into v_id_pessoa
      from tb_pessoa p;
 
   insert into tb_pessoa(idpessoa,nome,datanascimento)
                  values(v_id_pessoa,v_nome,v_dt_nascimento);

 end loop;
end;

commit;

03. Criação dos objetos

Vamos criar as procedures com as seguintes formas de cursores. Vejam que em todas temos as variáveis v_dt_inicio e v_dt_fim, que farão o controle do tempo de processamento da procedure.

O cursor contido nesta procedure é o mais usual. O cursor de tipo pré-declarado

Procedure PRC_TST_CURSOR

create or replace procedure prc_tst_cursor is
v_dt_inicio timestamp;
v_dt_fim    timestamp;
cursor cr_pessoa is
select *
 from tb_pessoa;
v_pessoa cr_pessoa%rowtype;

begin
 v_dt_inicio := systimestamp;
 open cr_pessoa;
  loop
  fetch cr_pessoa into v_pessoa;
  exit when cr_pessoa%notfound;
    null;
  end loop;
 close cr_pessoa;
 v_dt_fim := systimestamp;
 dbms_output.put_line('Tempo processamento: '||to_char(v_dt_fim - v_dt_inicio,'dd/mm/yyyy hh24:mi:ss.ff'));

end;

Esta procedure abaixo contém uma forma de cursor bastante utilizada também. O cursor do tipo for é um dos de mais fácil de utilização.

Procedure PRC_TST_FOR

create or replace procedure prc_tst_for is
v_dt_inicio timestamp;
v_dt_fim    timestamp;

begin
 v_dt_inicio := systimestamp;
 for dd in (select p.idpessoa
              from tb_pessoa p) loop
    null;
  end loop;

 v_dt_fim := systimestamp;
 dbms_output.put_line('Tempo processamento: '||to_char(v_dt_fim - v_dt_inicio,'dd/mm/yyyy hh24:mi:ss.ff'));
 
end;

Nesta procedure abaixo, temos a extração da informação através de um bulk collection. Forma menos usual, alguns front end não suportam este tipo de cursor, que é o caso do FORMS.

Procedure PRC_TST_BULK_COUNT

create or replace procedure prc_tst_bulk_count is
v_dt_inicio timestamp;
v_dt_fim    timestamp;
type typ_pessoa is table of tb_pessoa%rowtype;
v_tb_pessoa typ_pessoa;

begin
 v_dt_inicio := systimestamp;

 select *
   bulk collect into v_tb_pessoa
   from tb_pessoa;
  for dd in 1..v_tb_pessoa.count loop
    null;
  end loop;

 v_dt_fim := systimestamp;
 dbms_output.put_line('Tempo processamento: '||to_char(v_dt_fim - v_dt_inicio,'dd/mm/yyyy hh24:mi:ss.ff'));

end;

Nesta procedure abaixo, temos a variação do cursor de bulk collection, onde a diferença está apenas na execução do loop.

Procedure PRC_TST_BULK_FIRST_LAST

create or replace procedure prc_tst_bulk_first_last is
v_dt_inicio timestamp;
v_dt_fim    timestamp;
type typ_pessoa is table of tb_pessoa%rowtype;
v_tb_pessoa typ_pessoa;

begin
 v_dt_inicio := systimestamp;
 select *
   bulk collect into v_tb_pessoa
   from tb_pessoa;
  for dd in v_tb_pessoa.first.. v_tb_pessoa.last loop
    null;
  end loop;
 v_dt_fim := systimestamp;
 dbms_output.put_line('Tempo processamento: '||to_char(v_dt_fim - v_dt_inicio,'dd/mm/yyyy hh24:mi:ss.ff'));
 
end;

Para a execução das procedures utilizei o PL/SQL Developer, para isso você pode utilizar qualquer programa, SQL*Plus, TOAD, SQL Developer dentre outros.

1

Chamada da procedure prc_tst_cursor.

2

Tempo de execução prc_tst_cursor.

3

Chamada da procedure prc_tst_for.

4

Tempo de execução prc_tst_for.

5

Chamada da procedure prc_tst_bulk_count.

6

Tempo de execução prc_tst_bulk_count.

7

Chamada da procedure prc_tst_bulk_first_last.

8

Tempo de execução prc_tst_bulk_first_last.

Procedure    Tempo Execução

PRC_TST_CURSOR     953 Milésimos

PRC_TST_FOR     844 Milésimos

PRC_TST_BULK_COUNT     109 Milésimos

PRC_TST_BULK_FIRST_LAST 109 Milésimos

Conclusões

Velocidade

Com isso, conseguimos provar que a análise do cursor explicito (FETCH) é o mais lento levando 953 Milésimos para sua execução, seguido do cursor implícito (FOR) com 844 Milesimos.

E em primeiro lugar cursores de BULK COLLECT. Independente da forma de extração dos dados de dentro do cursor, da montagem do loop, sendo via COUNT ou via FIRST-LAST, são os mais rápidos ambas as formas levando 109 Milésimospara seu processamento.

Facilidade de Implementação

No ponto de vista de implementação dos cursores, o FOR é os mais simples, bastando ser declarado ao longo do programa. Os cursores de BULK COLLECT vêm segundo lugar, pois necessitam que sejam declarados variáveis baseadas em types, tornando a implementação pouca coisa mais trabalhosa. Já os cursores de FETCH vêm em último lugar. Como devem ser declarados juntamente com as variáveis que receberão os valores, além de que devem abertos ao longo da execução do programa e fechados, sua implementação torna-se mais trabalhosa.

Documentação

Todos os cursores podem ser documentados com facilidade. Por sintaxe o cursor de FETCH deve receber obrigatoriamente um nome em sua declaração, tornando a documentação automática. Os outros cursores também podem ser nomeados, mas não é muito comum de ver esse tipo de coisa.

Conclusão Final

Com base na velocidade, o mais importante, nas facilidades de implementação e documentações. Concluo que em primeiro lugar por conter a execução mais rápida e estar em segundo lugar em facilidade de implementação o cursor de BULK COLLECT é o melhor a ser utilizado para uma massa de dados grande.

Considero também que os outros cursores também são de grande utilização, mas para massas de dados menores.

Abraços

 

Leonardo Litz

Leonardo Litz

É formado em Análise de Sistemas pela Uniban. Possui 9 anos de experiência em análise, implementação e desenvolvimento de softwares com Oracle Forms/Reports, PL/SQL. Também possui experiência de 5 anos em WebTool Kit, HTML, JavaScript, XML, CSS e APEX, além de conhecimentos em JAVA e Delphi. Possui certificação Oracle Advanced PL/SQL Developer Certified Professional 11g. Em sua experiência profissional teve a oportunidade de participar de diversos projetos, dos quais pode-se destacar migrações de sistemas de arquivos indexados em Cobol para banco de dados Oracle; tunning em camada de aplicações e camada de banco de dados; administração de banco de dados Oracle 9i e 10g; modelagem relacional de dados utilizando Erwin; migração do Forms 6i para Forms IAS 10g; levantamento, análise e desenvolvimento de software em Delphi com Oracle, Oracle WebTool Kit e APEX 4.0.1. Atualmente trabalha em uma empresa petroquimica, na qual atua como Desenvolvedor Oracle EBS (OA 11.5.10), desenvolvendo customizações para todos os módulos, nos padrões e recursos do ERP, utilizando PL/Sql, Forms 6i, Reports 6i, Discover, WorkFlow e APEX.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

plugins premium WordPress