Pular para o conteúdo

Oracle – PIPELINED Table Function

Olá!

Este é um pequeno artigo sobre o uso de um recurso do banco de dados Oracle que poucas pessoas conhecem, o nome desse recurso é PIPELINED Table Function ou simplesmente PIPELINED Function.
As ferramentas utilizadas nas implementações foram o Oracle SQL Developer e o Oracle 10g Express Edition rodam em Windows Vista.

Smartphone VoIP Livros de Java Apartamentos

###############
1. INTRODUÇÃO

Uma PIPELINED Table Function é uma função capaz de devolver várias linhas de resultados e são chamadas na cláusula FROM de uma instrução SELECT como se fosse uma tabela.
Nada melhor para aprender um novo recurso do que um exemplo bem simples.

O exemplo abaixo implementa um PIPELINED function que devolve a quantidade de linhas informada no parametro qtd_num.

[sourcecode lang="sql"]CREATE OR REPLACE PACKAGE pac_fun_PIPELINED AS

type numeros IS TABLE OF NUMBER;

FUNCTION fun_gera_numeros(qtd_num number) RETURN numeros PIPELINED;

END pac_fun_PIPELINED;

CREATE OR REPLACE PACKAGE BODY pac_fun_PIPELINED AS

FUNCTION fun_gera_numeros(qtd_num NUMBER) RETURN numeros PIPELINED IS 
BEGIN  
   FOR cont IN 1 .. qtd_num  LOOP    
       pipe ROW(cont * cont); -- Eleva ao quadrado  

   END LOOP;

  RETURN;

END fun_gera_numeros;

END pac_fun_PIPELINED;[/sourcecode]

Para executar a function use a instrução SELECT abaixo:

[sourcecode lang="sql"]SELECT * FROM TABLE(pac_fun_PIPELINED.fun_gera_numeros(9));[/sourcecode]

O resultado é exibido na figura abaixo.

Viu porque essa function pode ser chamada de “virtual table”?

Três itens nesse exemplo são caracteristicas de uma PIPELINED function:
1. A palavra-chave PIPELINED;
2. O comando PIPE ROW que joga o registro no result set;
3. E o comando RETURN; em “branco” apesar do tipo de retorno da function indicar numeros.

Geralmente os types e as functions são implementadas dentra de uma package, nada impede que você as crie fora. Só a titulo de curiosidade, quando uma package é solicitada, tudo o que foi implementada dentro dela sobe para a memória. Então, dependendo das suas necessidades não é interessante implementar uma package.

Apesar de ser permitido, no exemplo não foi indicado se o paramêtro da function é de entrada (IN) ou de saída (OUT).

A vantagem entre uma função PIPELINED e outra não PIPELINED é que a função PIPELINED processa os dados e joga os resultados imediatamente para o result set do SELECT, assim o chamador pode usar os dados mais rapidamente sem ter que esperar todo o processamento.
por outro lado a função que não é PIPELINED processa os dados e vai acumulando os resultados para depois devolver(usando a cláusula RETURN ou usando paramêtro de OUT) os resultados para chamador da função, só então ele poderá usar os dados.

###############
2. USANDO TIPOS DEFINIDOS

Agora será implementado um exemplo usando tipos definidos pelo programador.
Neste exemplo será criado uma function que calcula a tabuada de um número (num_tabuada).

[sourcecode lang="sql"]create or replacePACKAGE pac_fun_PIPELINED AS

type registro is record (  indice number,  multiplicador number,  resultado number);

type tabuada IS TABLE OF registro;

FUNCTION fun_tabuada(num_tabuada    number, qtd_resultados number)RETURN tabuada PIPELINED;

END pac_fun_PIPELINED;

create or replacePACKAGE BODY pac_fun_PIPELINED AS

FUNCTION fun_tabuada(num_tabuada    number, qtd_resultados number)RETURN tabuada 
  PIPELINED ISaux registro;
 BEGIN

  FOR cont IN 1 .. qtd_resultados  LOOP    
        aux.indice := cont;    
        aux.multiplicador := num_tabuada;    
        aux.resultado := cont * num_tabuada;    
        pipe ROW(aux);  
  END LOOP;

  RETURN;

END fun_tabuada;

END pac_fun_PIPELINED;[/sourcecode]

Para calcular a tabuada do 7, a function será executada com o comando abaixo:

[sourcecode lang="sql"]SELECT * FROM TABLE(pac_fun_PIPELINED.fun_tabuada(7, 10));[/sourcecode]

O resultado é exibido na figura abaixo.

#############################
3. USANDO JUNTO COM TABELAS DE VERDADE

Para este exemplo será necessário criar uma tabela, dois tipo, uma função e uma package

[sourcecode lang="sql"]CREATE TABLE "CAD_PEDIDO_TABUADA"( "NUM_TABUADA" NUMBER NOT NULL ENABLE, "DATA_PEDIDO" DATE NOT NULL ENABLE, "QTD_LINHAS" NUMBER, "CODIGO" NUMBER,  CONSTRAINT "PK_CAD_PEDIDO_TABUADA" PRIMARY KEY ("CODIGO")USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)TABLESPACE "USERS"  ENABLE ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGINGSTORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)TABLESPACE "USERS" ;[/sourcecode]

Inserindo dados na tabela.

[sourcecode lang="sql"]INSERT INTO "CAD_PEDIDO_TABUADA" (NUM_TABUADA, DATA_PEDIDO, QTD_LINHAS, CODIGO) VALUES ('7', TO_DATE('05/05/08', 'DD/MM/RR'), '5', '2');INSERT INTO "CAD_PEDIDO_TABUADA" (NUM_TABUADA, DATA_PEDIDO, QTD_LINHAS, CODIGO) VALUES ('2', TO_DATE('05/05/08', 'DD/MM/RR'), '11', '1');[/sourcecode]

Abaixo a package com tudo o que foi criado até agora para os exemplos.

[sourcecode lang="sql"]CREATE OR REPLACE PACKAGE pac_fun_PIPELINED AS

-- Exemplo 1type numeros IS TABLE OF NUMBER;

-- Exemplo 2type registro IS record(indice NUMBER,   multiplicador NUMBER,   resultado NUMBER);type tabuada IS TABLE OF registro;

-- Exemplo 3type pedido_tabuada IS TABLE OF cad_pedido_tabuada % rowtype;TYPE tab_refcursor IS REF CURSOR RETURN cad_pedido_tabuada%ROWTYPE;

----------------------------------------------------------  -- Exemplo 1FUNCTION fun_gera_numeros(qtd_num IN NUMBER) RETURN numeros PIPELINED;

-- Exemplo 2FUNCTION fun_tabuada(num_tabuada NUMBER,   qtd_resultados NUMBER) RETURN tabuada PIPELINED;

-- Exemplo 3FUNCTION fun_cad_tabuada(cursor_tabuada in tab_refcursor) RETURN tabuada PIPELINED;

END pac_fun_PIPELINED;

create o
r replacePACKAGE BODY pac_fun_PIPELINED AS

FUNCTION fun_gera_numeros(qtd_num in NUMBER) RETURN numeros PIPELINED ISBEGIN  FOR cont IN 1 .. qtd_num  LOOP    pipe ROW(cont * cont); -- Eleva ao quadrado  END LOOP;

  RETURN;

END fun_gera_numeros;

FUNCTION fun_tabuada(num_tabuada    number,                     qtd_resultados number)RETURN tabuada PIPELINED ISaux registro;BEGIN

  FOR cont IN 1 .. qtd_resultados  LOOP    aux.indice := cont;    aux.multiplicador := num_tabuada;    aux.resultado := cont * num_tabuada;    pipe ROW(aux);  END LOOP;

  RETURN;

END fun_tabuada;

FUNCTION fun_cad_tabuada(cursor_tabuada in tab_refcursor) RETURN tabuada PIPELINED IS  temp cad_pedido_tabuada%rowtype;  aux registro;  erro varchar2(500) := 'ERRO: ';BEGIN

  loop      fetch cursor_tabuada into temp;      exit when cursor_tabuada%notfound;

        -- Calcula a tabuada até a quantidade de linhas cadastradas.        for contador IN 1..temp.qtd_linhas loop          aux.indice := contador;          aux.multiplicador := temp.NUM_TABUADA;          aux.resultado := contador * temp.NUM_TABUADA;

          PIPE ROW (aux);        end loop;

  end loop;

EXCEPTION  WHEN OTHERS THEN  DBMS_OUTPUT.PUT_LINE(erro || sqlerrm);

END fun_cad_tabuada;

END pac_fun_PIPELINED;[/sourcecode]

Para executar a function use a instrução abaixo:

[sourcecode lang="sql"]SELECT indice, multiplicador, resultadoFROM TABLE(pac_fun_PIPELINED.fun_cad_tabuada(cursor(select NUM_TABUADA,DATA_PEDIDO,DATA_PROCESS,QTD_LINHAS,CODIGO       from CAD_PEDIDO_TABUADA       where codigo = 1)))[/sourcecode]

O resultado é exibido na figura abaixo.

Não foi possível percorrer todos os registros da tabela cad_pedido_tabuada retirando a claúsula WHERE, o Oracle sempre processa o primeiro registro somente. Não descobri porque é assim e o motivo dessa limitação.
Não foi possível dar commit dentro da function, também não descobri por que razão a Oracle limitou o uso desse recurso.

###############
4. PARALLEL PIPELINED FUNCTIONS

As funções PIPELINED podem se tornar ainda mais poderosas, pois são paralelizáveis, com isso o processamento dos dados ficam mais rápidos.
Veja nesse link como funciona as PIPELINED and parallel functions.
Para utilizar os recursos de paralelização é preciso seguir algumas regras.
a) A função deve ser assinada com a palavra-chave PARALLEL_ENABLE;
b) A função tem de ser alimentada com um ref cursor.
Esse tópico é apenas para divulgação, até porque eu não possuo conhecimento sobre esse recurso do Oracle.
Para saber mais recomendo acessar o link: Parallel Table Functions.

###############
5. LINKS PARA SABER MAIS

http://www.codeguru.com/cpp/data/mfc_database/oracle/article.php/c4285/
http://download.oracle.com/docs/cd/B14117_01/appdev.101/b10800/dcitblfns.htm

http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28425/pipe_paral_tbl.htm
http://download.oracle.com/docs/cd/B14117_01/appdev.101/b10800/dcitblfnxemp.htm

Até mais,

Deixe um comentário

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