Not only developing

luglio 20, 2009

Utilizzo delle collezioni in Oracle-PL/SQL

Filed under: Oracle — Etichette: , — teox78 @ 12:56 pm

Le collezioni di Oracle semplificano sicuramente la programmazione in PL/SQL e sono in grado di velocizzare notevolmente alcuni processi.

Mostrerò come utilizzarle e includerò qualche esempio di impieghi particolari.

Innanzitutto creiamo il tipo

Create or replace type t_varchar2_table as table of varchar2(4000);

Operazioni base :

Create or replace procedure pr_base_test AS
--inizializzo la lista con zero elementi
x_Varchar2_Table t_varchar2_table := t_varchar2_table();
n_Step NUMBER;
BEGIN
--mettiamo 10 elementi nella lista
FOR n_Step IN 1..10 LOOP
x_Varchar2_Table.EXTEND;
x_Varchar2_Table(x_Varchar2_Table.LAST) := 'Row '||n_Step;
END LOOP;
--visualizzo quanti elementi ho nella lista
Dbms_Output.put_Line('Count = '||x_Varchar2_Table.COUNT);
--visualizzo ogni elemento in lista
FOR n_Step IN x_Varchar2_Table.FIRST..x_Varchar2_Table.LAST LOOP
Dbms_Output.put_Line(x_Varchar2_Table(n_Step);
END LOOP;
--controllo se un elemento è presente nella lista
IF 'Row 7' MEMBER OF x_Varchar2_Table THEN
Dbms_output.put_Line('OK');
END IF;
--Controllo se la lista è univoca, ovvero non ho duplicati
IF x_Varchar2_Table IS A SET THEN
Dbms_output.put_Line('IS A SET);
END IF;
--svuoto la lista
x_Varchar2_Table.DELETE;
END;
/

A partire da Oracle 10g sono presenti una serie di operatori multiset che permettono operazioni sulle liste. Eccone una lista con accanto l’equivalente operatore SQL :

MULTISET UNION DISTINCT UNION
MULTISET UNION UNION ALL
MULTISET UNION ALL UNION ALL
MULTISET INTERSECT INTERSECT
MULTISET INTERSECT DISTINCT N/A
MULTISET EXCEPT MINUS
MULTISET EXCEPT DISTINCT N/A

Per riempire una lista con dei valori provenienti da una query è semplicissimo :

declare
x_table_list t_varchar2_table := t_varchar2_table();
begin
select table_name BULK COLLECT INTO x_table_list
from user_tables
where table_name like 'A%';
--la lista ora contiene l'elenco delle tabelle che iniziano con A
end;

Naturalmente non bisogna abusare dell’uso delle liste in quanto consumano parecchia memoria. Gli utilizzi principali che ho riscontrato sono :

  • creo una lista di dati che poi andrò ad utilizzare parecchie volte : è inutile fare la join con la medesima tabella (magari di grandi dimensioni) molte volte quando è possibile caricare in memoria la lista di ID di cui necessito e unitilizzarla n volte.
  • sono molto utili per trasferire dati da una procedura all’altra : ad esempio, se ho un package che mi gestisce gli utenti e all’interno di questo una procedure che mi restituisce la lista degli utenti è inutile implementare tale logica in un altro package. Quindi il package che avrà bisogno dell’elenco degli utenti invocherà tale procedura e ne utilizzerà la lista ottenuta.
  • Ottimizzazione di insert/update/delete di tante righe grazie all’operatore FORALL

Ecco alcuni esempi :

Esempio 1

declare
x_table_list t_varchar2_table := t_varchar2_table();
n_count Number;
begin
select table_name bulk collect into x_table_list
from user_tables
where table_name like 'A%';
--use the list as a table
select count(*) into n_count
from user_tab_columns a, TABLE(x_table_list) b
where a.table_name = b.column_value;
dbms_output.put_line(n_count);
end;

Esempio 2

create table temp
as select * from user_tables
where 1 = 2;

declare
type t_user_tables is table of user_tables%ROWTYPE;
x_user_tables t_user_tables := t_user_tables();
n Number;
begin
select * bulk collect into x_user_tables
from user_tables;
FORALL n IN x_user_tables.FIRST..x_user_tables.LAST
Insert into temp values x_user_tables(n);
COMMIT;
End;

E se invece avessi bisogno di utilizzare una collezione all’interno di una query dinamica ?? In questo caso è obbligatorio utilizzare collezioni basate su tipi creati all’interno dello schema tramite il comando Create Type. Nell’esempio utlizzerò il tipo t_varchar2_table creato all’inizio del post.


create or replace PACKAGE pk_Test AS
PROCEDURE pr_Load;
PROCEDURE pr_ExecuteQuery;

FUNCTION fn_List RETURN T_Varchar2_Table;

END  pk_Test;

create or replace PACKAGE BODY pk_Test AS
kx_List T_Varchar2_Table := T_Varchar2_Table();

PROCEDURE pr_Load IS
BEGIN
SELECT Table_Name BULK COLLECT INTO kx_List
FROM User_Tables
WHERE Table_Name LIKE ‘A%’;

END pr_Test;

PROCEDURE pr_ExecuteQuery IS
v_Sql VARCHAR2(1000);
n_Count NUMBER;
BEGIN
v_Sql := ‘SELECT COUNT(*)
FROM User_Tables
WHERE Table_Name IN (
SELECT Column_Value FROM
TABLE ( pk_Test.fn_List )
)’;

EXECUTE IMMEDIATE v_Sql INTO n_Count;
Dbms_Output.put_line(n_Count);
END pr_ExecuteQuery;

FUNCTION fn_List RETURN T_Varchar2_Table IS
BEGIN
Return kx_List;
END fn_List;

END pk_Test;

Nella procedura pr_Load non faccio altro che caricare in una collezione tutte le tabelle che iniziano con A.

Nella procedura pr_ExecuteQuery  eseguo una query dinamica tramite il comando Execute Immediate  utilizzando la lista appena caricata che mi viene restituita dalla funzione fn_List.

t_user_tables

Lascia un commento »

Non c'è ancora nessun commento.

RSS feed dei commenti a questo articolo. TrackBack URI

Lascia un Commento

Fill in your details below or click an icon to log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Log Out / Modifica )

Foto Twitter

You are commenting using your Twitter account. Log Out / Modifica )

Foto di Facebook

You are commenting using your Facebook account. Log Out / Modifica )

Connecting to %s

Tema: Silver is the New Black. Blog su WordPress.com.

Iscriviti

Get every new post delivered to your Inbox.