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