Oracle
 sql >> Database >  >> RDS >> Oracle

modi per evitare tabelle temporanee globali in Oracle

Rispondiamo prima alla seconda domanda:

"perché andare via dai GTT? sono davvero così brutti."

Un paio di giorni fa stavo lanciando un proof of concept che caricava un file XML di grandi dimensioni (~ 18 MB) in un XMLType. Poiché non volevo archiviare XMLType in modo permanente, ho provato a caricarlo in una variabile PL/SQL (memoria di sessione) e in una tabella temporanea. Il caricamento in una tabella temporanea ha richiesto cinque volte il tempo necessario per caricarlo in una variabile XMLType (5 secondi rispetto a 1 secondo). La differenza è perché le tabelle temporanee non sono strutture di memoria:vengono scritte su disco (in particolare il tablespace temporaneo nominato).

Se vuoi memorizzare nella cache molti dati, salvarli in memoria stresserà il PGA, il che non va bene se hai molte sessioni. Quindi è un compromesso tra RAM e tempo.

Alla prima domanda:

"Qualcuno può mostrare come trasformare le query di esempio sopra in raccolte e/o cursori?"

Le query che pubblichi possono essere unite in un'unica istruzione:

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'));

(Ho semplicemente trasposto la tua logica ma quel case() potrebbe essere sostituita con una più ordinata nvl2(trim(a.column_a), a.column_a, b.data_a) ).

So che dici che le tue domande sono più complicate, ma il tuo primo punto di riferimento dovrebbe essere prendere in considerazione la possibilità di riscriverle. So quanto sia seducente spezzare una query nodosa in molti piccoli SQL cuciti insieme a PL/SQL, ma l'SQL puro è molto più efficiente.

Per utilizzare una raccolta è meglio definire i tipi in SQL, perché ci dà la flessibilità di usarli nelle istruzioni SQL così come in 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;
/

Ecco una funzione di esempio, che restituisce un set di risultati:

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; 
/ 

Ed eccolo in azione:

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>

Nella funzione è necessario istanziare il tipo con le colonne, in modo da evitare l'eccezione ORA-00947. Questo non è necessario quando si compila un tipo di tabella 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> 

Infine, le linee guida

"Quali dovrebbero essere le linee guida su Quando usare e Quando evitare le GTT"

Le tabelle temporanee globali sono molto buone quando abbiamo bisogno di condividere i dati memorizzati nella cache tra diverse unità di programma nella stessa sessione. Ad esempio se abbiamo una struttura di report generica generata da una singola funzione che alimenta un GTT che è popolato da una delle numerose procedure. (Anche se anche quello potrebbe essere implementato anche con cursori di riferimento dinamici ...)

Le tabelle temporanee globali sono utili anche se abbiamo molte elaborazioni intermedie che sono semplicemente troppo complicate per essere risolte con una singola query SQL. Soprattutto se tale elaborazione deve essere applicata a sottoinsiemi delle righe recuperate.

Ma in generale la presunzione dovrebbe essere che non abbiamo bisogno di usare una tabella temporanea. Quindi

  1. Fallo in SQL a meno che non sia troppo difficile in quale caso ...
  2. ... Fallo in variabili PL/SQL (di solito raccolte) a meno che non richieda troppa memoria in questo caso ...
  3. ... Fallo con una tabella temporanea globale