Oracle
 sql >> Base de données >  >> RDS >> Oracle

façons d'éviter les tables temporaires globales dans oracle

Répondons d'abord à la deuxième question :

"pourquoi s'éloigner des GTT ? sont-ils vraiment si mauvais."

Il y a quelques jours, j'étais en train de créer une preuve de concept qui chargeait un fichier XML assez volumineux (~ 18 Mo) dans un XMLType. Parce que je ne voulais pas stocker le XMLType de manière permanente, j'ai essayé de le charger dans une variable PL/SQL (mémoire de session) et une table temporaire. Le charger dans une table temporaire a pris cinq fois plus de temps que de le charger dans une variable XMLType (5 secondes contre 1 seconde). La différence est que les tables temporaires ne sont pas des structures de mémoire :elles sont écrites sur le disque (en particulier votre espace de table temporaire désigné).

Si vous souhaitez mettre en cache beaucoup de données, les stocker en mémoire stressera le PGA, ce qui n'est pas bon si vous avez beaucoup de sessions. C'est donc un compromis entre la RAM et le temps.

À la première question :

"Quelqu'un peut-il montrer comment transformer les exemples de requêtes ci-dessus en collections et/ou en curseurs ?"

Les requêtes que vous publiez peuvent être fusionnées en une seule déclaration :

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

(J'ai simplement transposé votre logique mais que case() l'instruction pourrait être remplacée par un nvl2(trim(a.column_a), a.column_a, b.data_a) plus propre ).

Je sais que vous dites que vos requêtes sont plus compliquées, mais votre première escale devrait être d'envisager de les réécrire. Je sais à quel point il est séduisant de décomposer une requête complexe en de nombreux bébés SQL assemblés avec du PL/SQL, mais le SQL pur est bien plus efficace.

Pour utiliser une collection, il est préférable de définir les types en SQL, car cela nous donne la possibilité de les utiliser dans des instructions SQL ainsi que dans 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;
/

Voici un exemple de fonction, qui renvoie un ensemble de résultats :

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

Et le voici en action :

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>

Dans la fonction, il est nécessaire d'instancier le type avec les colonnes, afin d'éviter l'exception ORA-00947. Ceci n'est pas nécessaire lors du remplissage d'un type de table 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> 

Enfin, les consignes

"Quelles devraient être les directives sur Quand utiliser et Quand éviter les GTT"

Les tables temporaires globales sont très utiles lorsque nous avons besoin de partager des données mises en cache entre différentes unités de programme au cours de la même session. Par exemple, si nous avons une structure de rapport générique générée par une seule fonction se nourrissant d'un GTT qui est rempli par l'une de plusieurs procédures. (Bien que même cela puisse également être implémenté avec des curseurs de référence dynamiques ...)

Les tables temporaires globales sont également utiles si nous avons beaucoup de traitements intermédiaires trop compliqués pour être résolus avec une seule requête SQL. Surtout si ce traitement doit être appliqué à des sous-ensembles des lignes récupérées.

Mais en général, la présomption devrait être que nous n'avons pas besoin d'utiliser une table temporaire. Alors

  1. Faites-le en SQL à moins que ce ne soit trop difficile auquel cas...
  2. ... Faites-le dans des variables PL/SQL (généralement des collections) sauf si cela prend trop de mémoire, auquel cas ...
  3. ... Faites-le avec une table temporaire globale