Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Möglichkeiten zur Vermeidung globaler temporärer Tabellen in Oracle

Lassen Sie uns zuerst die zweite Frage beantworten:

"Warum von den GTTs weggehen? Sind sie wirklich so schlecht."

Vor ein paar Tagen habe ich einen Proof of Concept erstellt, der eine große XML-Datei (~ 18 MB) in einen XMLType geladen hat. Da ich den XMLType nicht dauerhaft speichern wollte, habe ich versucht, ihn in eine PL/SQL-Variable (Sitzungsspeicher) und eine temporäre Tabelle zu laden. Das Laden in eine temporäre Tabelle dauerte fünfmal so lange wie das Laden in eine XMLType-Variable (5 Sekunden im Vergleich zu 1 Sekunde). Der Unterschied besteht darin, dass temporäre Tabellen keine Speicherstrukturen sind:Sie werden auf die Festplatte geschrieben (insbesondere in Ihren nominierten temporären Tabellenbereich).

Wenn Sie viele Daten zwischenspeichern möchten, wird das Speichern im Speicher den PGA belasten, was nicht gut ist, wenn Sie viele Sitzungen haben. Es ist also ein Kompromiss zwischen RAM und Zeit.

Zur ersten Frage:

"Kann jemand zeigen, wie man die obigen Beispielabfragen in Sammlungen und/oder Cursor umwandelt?"

Die von Ihnen geposteten Abfragen können zu einer einzigen Anweisung zusammengeführt werden:

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

(Ich habe Ihre Logik einfach umgesetzt, aber dieser case() -Anweisung könnte durch ein übersichtlicheres nvl2(trim(a.column_a), a.column_a, b.data_a) ersetzt werden ).

Ich weiß, dass Sie sagen, dass Ihre Abfragen komplizierter sind, aber Ihre erste Anlaufstelle sollte sein, sie umzuschreiben. Ich weiß, wie verführerisch es ist, eine knorrige Abfrage in viele Baby-SQLs zu zerlegen, die mit PL/SQL zusammengefügt werden, aber reines SQL ist viel effizienter.

Um eine Sammlung zu verwenden, ist es am besten, die Typen in SQL zu definieren, da uns dies die Flexibilität gibt, sie sowohl in SQL-Anweisungen als auch in PL/SQL zu verwenden.

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

Hier ist eine Beispielfunktion, die eine Ergebnismenge zurückgibt:

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

Und hier ist es in Aktion:

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>

In der Funktion ist es notwendig, den Typ mit den Spalten zu instanziieren, um die Ausnahme ORA-00947 zu vermeiden. Dies ist beim Füllen eines PL/SQL-Tabellentyps nicht erforderlich:

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> 

Zum Schluss Richtlinien

„Welche Richtlinien sollten gelten, wann GTTs verwendet und wann vermieden werden sollten?“

Globale temporäre Tabellen sind sehr gut, wenn wir zwischengespeicherte Daten zwischen verschiedenen Programmeinheiten in derselben Sitzung gemeinsam nutzen müssen. Zum Beispiel, wenn wir eine generische Berichtsstruktur haben, die von einer einzelnen Funktion generiert wird, die aus einer GTT gespeist wird, die von einer von mehreren Prozeduren gefüllt wird. (Obwohl auch das mit dynamischen Ref-Cursorn realisierbar wäre ...)

Globale temporäre Tabellen sind auch gut, wenn wir viele Zwischenverarbeitungen haben, die einfach zu kompliziert sind, um sie mit einer einzigen SQL-Abfrage zu lösen. Vor allem, wenn diese Verarbeitung auf Teilmengen der abgerufenen Zeilen angewendet werden muss.

Aber im Allgemeinen sollte davon ausgegangen werden, dass wir keine temporäre Tabelle verwenden müssen. Also

  1. Machen Sie es in SQL, es sei denn, es ist zu schwierig, in welchem ​​​​Fall ...
  2. ... Tun Sie es in PL/SQL-Variablen (normalerweise Sammlungen), es sei denn, es nimmt zu viel Speicher in Anspruch, in diesem Fall ...
  3. ... Tun Sie es mit einer globalen temporären Tabelle