Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

maneiras de evitar tabelas temporárias globais no oracle

Vamos responder à segunda pergunta primeiro:


"por que ir embora dos GTTs? eles são tão ruins assim."

Alguns dias atrás, eu estava criando uma prova de conceito que carregava um arquivo XML grande (~ 18 MB) em um XMLType. Como não queria armazenar o XMLType permanentemente, tentei carregá-lo em uma variável PL/SQL (memória de sessão) e em uma tabela temporária. Carregar em uma tabela temporária demorou cinco vezes mais do que carregá-lo em uma variável XMLType (5 segundos em comparação com 1 segundo). A diferença é porque as tabelas temporárias não são estruturas de memória:elas são gravadas em disco (especificamente seu espaço de tabela temporário nomeado).

Se você quiser armazenar muitos dados em cache, armazená-los na memória sobrecarregará o PGA, o que não é bom se você tiver muitas sessões. Portanto, é uma troca entre RAM e tempo.

Para a primeira pergunta:


"Alguém pode mostrar como transformar as consultas de exemplo acima em coleções e/ou cursores?"

As consultas que você publica podem ser mescladas em uma única instrução:
SELECT case when a.column_a IS NULL OR a.column_a = ' ' 
           then b.data_a
           else  column_a end AS someA,
       a.someB,
       a.someC
FROM TABLE_A a
      left outer join TABLE_B b
          on ( a.column_b = b.data_b AND a.column_c = 'C' )
WHERE condition_1 = 'YN756'
  AND type_cd = 'P'
  AND TO_NUMBER(TO_CHAR(m_date, 'MM')) = '12'
  AND (lname LIKE (v_LnameUpper || '%') OR
  lname LIKE (v_searchLnameLower || '%'))
  AND (e_flag = 'Y' OR
  it_flag = 'Y' OR
  fit_flag = 'Y'));

(Eu simplesmente transpus sua lógica, mas esse case() pode ser substituído por um nvl2(trim(a.column_a), a.column_a, b.data_a) mais organizado ).

Eu sei que você diz que suas consultas são mais complicadas, mas seu primeiro porto de escala deve ser considerar reescrevê-las. Eu sei como é sedutor quebrar uma consulta complicada em muitos SQLs bebê costurados com PL/SQL, mas o SQL puro é muito mais eficiente.

Para usar uma coleção, é melhor definir os tipos em SQL, pois isso nos dá a flexibilidade de usá-los em instruções SQL, bem como em PL/SQL.
create or replace type tab_a_row as object
    (col_a number
     , col_b varchar2(23)
     , col_c date);
/
create or replace type tab_a_nt as table of tab_a_row;
/

Aqui está uma função de exemplo, que retorna um conjunto de resultados:
create or replace function get_table_a 
      (p_arg in number) 
      return sys_refcursor 
is 
    tab_a_recs tab_a_nt; 
    rv sys_refcursor; 
begin 
    select tab_a_row(col_a, col_b, col_c)  
    bulk collect into tab_a_recs 
    from table_a 
    where col_a = p_arg; 

    for i in tab_a_recs.first()..tab_a_recs.last() 
    loop 
        if tab_a_recs(i).col_b is null 
        then 
            tab_a_recs(i).col_b :=  'something'; 
        end if; 
    end loop;  

    open rv for select * from table(tab_a_recs); 
    return rv; 
end; 
/ 

E aqui está em ação:
SQL> select * from table_a
  2  /

     COL_A COL_B                   COL_C
---------- ----------------------- ---------
         1 whatever                13-JUN-10
         1                         12-JUN-10

SQL> var rc refcursor
SQL> exec :rc := get_table_a(1)

PL/SQL procedure successfully completed.

SQL> print rc

     COL_A COL_B                   COL_C
---------- ----------------------- ---------
         1 whatever                13-JUN-10
         1 something               12-JUN-10

SQL>

Na função é necessário instanciar o tipo com as colunas, para evitar a exceção ORA-00947. Isso não é necessário ao preencher um tipo de tabela PL/SQL:
SQL> create or replace procedure pop_table_a
  2        (p_arg in number)
  3  is
  4      type table_a_nt is table of table_a%rowtype;
  5      tab_a_recs table_a_nt;
  6  begin
  7      select *
  8      bulk collect into tab_a_recs
  9      from table_a
 10      where col_a = p_arg;
 11  end;
 12  /

Procedure created.

SQL> 

Finalmente, diretrizes


"Quais devem ser as orientações sobre quando usar e quando evitar GTT's"

As tabelas temporárias globais são muito boas quando precisamos compartilhar dados em cache entre diferentes unidades de programa na mesma sessão. Por exemplo, se tivermos uma estrutura de relatório genérica gerada por uma única função alimentando um GTT que é preenchido por um dos vários procedimentos. (Embora até isso também possa ser implementado com cursores de referência dinâmicos ...)

As tabelas temporárias globais também são boas se tivermos muito processamento intermediário que é muito complicado para ser resolvido com uma única consulta SQL. Especialmente se esse processamento deve ser aplicado a subconjuntos das linhas recuperadas.

Mas, em geral, deve-se presumir que não precisamos usar uma tabela temporária. então
  1. Faça isso em SQL, a menos que seja muito difícil, caso ...
  2. ... Faça isso em variáveis ​​PL/SQL (geralmente coleções), a menos que leve muita memória nesse caso ...
  3. ... Faça isso com uma tabela temporária global