a dati esterni con query SQL - Oracle Italia by Massimo Ruocchio

F O
C
U
S
Potenza e versatilità dei Database
Con la versione 9i del database Oracle è
possibile leggere un file esterno di dati come se
fosse una tabella relazionale. Vediamo come
Accedere
a dati esterni
con query SQL
O
gni mattina la stessa storia. Da quasi
tre anni. Arrivo in ufficio quando i miei
colleghi stanno ancora dormendo. Sono
il responsabile del “centro elaborazione
dati” di un’azienda non molto grande che vende,
grazie ad Internet, in tutto il mondo. La mattina
arrivo in ufficio e carico nel database Oracle i dati
degli ordini ricevuti durante la notte ed il giorno precedente. Le applicazioni per la ricezione degli ordini,
il sito Internet e l’applicazione utilizzata dai colleghi
del call center, non sono basate sul database Oracle.
Scrivono i dati in un file ASCII, io effettuo l’import in
una tabella relazionale per consentire al magazzino ed
all’ufficio clienti di organizzare le spedizioni.
Lo so, è un pessimo esempio d’integrazione d’applicazioni, ma che ci volete fare… quando i sistemi
informatici vengono costruiti a pezzi nel corso degli
anni, questi sono i risultati. Quando ho raccontato
questa storia ad un amico molto esperto di Oracle –
pensate che lo chiamano “l’Oracolo”! – mi ha consigliato di passare alla versione 9i del Database e di utilizzare una External Table. Ho fatto come mi aveva
consigliato. Se v’interessa quello che ho scoperto continuate a leggere quest’articolo…
di Massimo Ruocchio
[email protected]
È laureato in matematica ed è certificato Oracle
Application Developer. Si occupa di analisi, progettazione e sviluppo di applicazioni software
Al primo impatto con la definizione sorge subito una
perplessità legata alle prestazioni. Più in basso abbiamo
dedicato un paragrafo a quest’argomento. Nel prossimo
paragrafo cercheremo di familiarizzare con la sintassi
che si utilizza per definire una External Table.
Nel paragrafo successivo introdurremo un esempio.
La sintassi
Una External Table si crea con l’istruzione CREATE
TABLE. La clausola da utilizzare per indicare che
i dati della tabella risiedono esternamente al database è ORGANIZATION EXTERNAL. All’interno
di questa clausola bisogna indicare:
In generale…
Una External Table è una tabella definita nel database
ma i cui dati sono conservati in un file ASCII posto
fuori del database. La External Table può essere letta
esattamente come ogni altra tabella relazionale ma non
può essere modificata. La logica è piuttosto semplice.
Nella definizione della tabella s’includono (utilizzando
la sintassi dei control file di SQL*Loader) le informazioni su come leggere il file che contiene i dati.
Quando si esegue un’istruzione SQL che coinvolge la
External Table viene eseguito al volo un import dei dati
in memoria. I dati memorizzati vengono utilizzati come
se fossero provenienti da una tabella memorizzata.
Il tipo di dati esterni che si utilizzano (parola
chiave TYPE)
La directory in cui si trovano i dati esterni (parola
chiave DEFAULT DIRECTORY)
I parametri con cui è possibile accedere ai dati
(parola chiave ACCESS PARAMETERS)
La posizione fisica dei dati (parola chiave
LOCATION)
Vediamo più in dettaglio come valorizzare questi
parametri. In teoria i dati da leggere come External
Table possono essere conservati in un data source di
qualunque tipo. In pratica, per leggere i dati dal data
source esterno, e modellarli in memoria in forma rela-
41
CP 116
F O
C
U
S
Potenza e versatilità dei Database
LISTATO 1 Le prime righe del file comuni.txt
CASAPE
RM00010B932
GALLICANO NEL LAZIO
RM00010D875
MARCELLINA
RM00010E924
MONTEFLAVIO
RM00010F504
MONTELIBRETTI
RM00010F545
MONTORIO ROMANO
RM00010F692
MORICONE
RM00010F730
POLI
RM00010G784
SAN POLO DEI CAVALIERI
RM00010I125
SANT’ANGELO ROMANO
RM00010I284
MENTANA
RM00013F127
MONTECELIO
RM00014F466
MONTEROTONDO
RM00015F611
NEROLA
RM00017F871
PALOMBARA SABINA
RM00018G293
TIVOLI
RM00019L182
AGOSTA
RM00020A084
ARCINAZZO ROMANO
RM00020A370
CAMERATA NUOVA
RM00020B472
CANTERANO
RM00020B635
CERRETO LAZIALE
RM00020C518
CERVARA DI ROMA
RM00020C543
CICILIANO
RM00020C677
CINETO ROMANO
RM00020C702
MANDELA
RM00020B632
PERCILE
RM00020G444
PISONIANO
RM00020G704
ROCCA CANTERANO
RM00020H387
ROCCAGIOVINE
RM00020H411
....................................
zionale, il database ha bisogno di un driver adatto al
data source utilizzato. La parola chiave TYPE permette
di selezionare il driver da utilizzare. Oracle mette a
disposizione un solo driver, quello che consente di leggere i dati immagazzinati in un file ASCII mediante
un accesso del tutto simile a quello effettuato da
SQL*Loader. Altri driver per accedere a diversi tipi di
data source devono essere scritti direttamente da chi
intende utilizzarli. Quindi l’unico valore ammesso per
TYPE è ORACLE_LOADER, a meno di scriversi un
driver da soli per accedere ad altro tipo di dati.
Nella specifica della directory da cui leggere i dati
non bisogna indicare direttamente il percorso ma un
identificatore di directory definito in Oracle. La creazione di un identificatore di directory si realizza con
l’istruzione
Create directory nomedir as ‘percorso’;
42
CP 116
Una volta creata la directory in Oracle, essa può
essere utilizzata come DEFAULT DIRECTORY nella
creazione della External Table.
Mediante la clausola ACCESS PARAMETERS si
specifica la struttura del file ASCII che contiene i dati.
La sintassi e le potenzialità sono esattamente le stesse
dei control file che si utilizzano per SQL*Loader. È
possibile specificare se i record ed i singoli campi sono
a lunghezza fissa o terminati da un certo carattere, i
tipi ed i formati dei singoli campi, i nomi dei file di Log
e dei record scartati o malformati, eccetera. Per una
definizione precisa dei parametri d’accesso si può fare
riferimento al manuale di SQL*Loader.
Mediante LOCATION si definisce il nome del file,
o dei file, che contiene i dati. È possibile indicare
direttamente il nome del file se esso è contenuto nella
directory di default, altrimenti è possibile utilizzare la
sintassi DIRECTORY:’FILE’. Questa è la teoria, nel
prossimo paragrafo vedremo un esempio pratico.
Un esempio
Per fare un po’ di pratica creiamo una External Table
basata su un file contenente i comuni italiani. Il file si
chiama comuni.txt e supponiamo di posizionarlo nella
directory c:\documenti\articoli\oraexternal. Nel
Listato 1 sono rappresentati i primi record del file.
Ogni record è formato dal nome del comune (25
caratteri), la provincia (2), il CAP (5) ed il codice
ISTAT (4). I campi non sono delimitati da alcun
carattere. Prima di creare la tabella bisogna definire la
directory. Eseguiamo quindi l’istruzione
Create directory ext_data_dir
as ‘c:\documenti\articoli\oraexternal’;
Per controllare le directory già esistenti si può effettuare una query dalle tabelle ALL_DIRECTORIES
oppure DBA_DIRECTORIES.
Ecco l’istruzione per creare la tabella esterna:
create table comuni_esterna
(comune
varchar2(25),
provincia
char(2),
CAP
char(5),
codice_istat char(4)
)
ORGANIZATION EXTERNAL
(
TYPE oracle_loader
DEFAULT DIRECTORY ext_data_dir
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
LOGFILE ext_data_dir:’comuni_ext.log’
FIELDS (
Comune
POSITION(1:25) CHAR(25)
NULLIF Comune=BLANKS,
Provincia
POSITION(26:27) CHAR(2)
NULLIF Provincia=BLANKS,
CAP
POSITION(28:32) CHAR(5)
NULLIF CAP=BLANKS,
Codice_ISTAT POSITION(33:36) CHAR(4)
NULLIF codice_ISTAT=BLANKS
)
)
LOCATION (‘comuni.txt’)
)
PARALLEL;
In accordo con quanto detto nel paragrafo precedente abbiamo prima definito la struttura della
tabella e poi, mediante ORGANIZATION EXTERNAL, abbiamo dato le indicazioni su come recuperare
i dati. Abbiamo indicato che ogni record occupa
una linea del file ed abbiamo definito il nome del
file di Log.
Per ogni campo abbiamo indicato il tipo di dato, la
posizione ed abbiamo indicato di considerare null il
campo se nel file sono presenti solo dei blank.
Nel Listato 2 si può vedere il risultato della esecuzione del comando e delle prime select effettuate
sulla External Table.
A questo punto è possibile utilizzare, in sola lettura, la tabella comuni_esterna come una qualunque
altra tabella del sistema.
Se i campi fossero stati delimitati da virgola, e
quindi avessero avuto lunghezza variabile, avremmo
potuto sostituire la clausola FIELDS con la seguente
FIELDS TERMINATED BY ‘,’
(COMUNE, PROVINCIA, CAP, CODICE_ISTAT)
Nel prossimo paragrafo ci occuperemo delle prestazioni delle External Table.
LISTATO 2 I test sulle prestazioni hanno dato risultati tranquillizzanti
SQL> create table comuni_esterna
2
( comune
provincia
char(2),
4
CAP
char(5),
5
codice_istat
6
)
7
Prima di parlare di prestazioni è doverosa una premessa sulla configurazione hardware utilizzata per i
test. Tutte le prove sono state eseguite su un Pentium
III 600 con 64 Mb di RAM ed OS Windows 98. È
una configurazione ridicola, soprattutto se si considera che solo per installare Oracle 9i sono consigliati
96 Mb di RAM.
Nonostante la configurazione hardware utilizzata,
e nonostante l’impossibilità di indicizzare le External
Table, i test sulle prestazioni hanno dato risultati abbastanza incoraggianti.
Per migliorare sensibilmente le prestazioni
delle query che coinvolgono la tabella
COMUNI_ESTERNA, abbiamo aggiunto in fondo
all’istruzione di creazione della tabella la parola
chiave PARALLEL. Con questa opzione si autorizza
il DBMS a caricare i dati in memoria utilizzando più
sessioni di lavoro parallele.
Attiviamo la visualizzazione dei tempi d’esecuzione
delle query mediante il comando SET TIMING ON e
facciamo qualche prova. Con i dati contenuti nel
file comuni.txt ho creato una tabella classica (utilizzando SQL*Loader) che si chiama comuni ed è identica alla nostra COMUNI_ESTERNA. Cominciamo
con le due query più banali:
SQL> select count(0)
2 from comuni;
COUNT(0)
---------8162
char(4)
ORGANIZATION EXTERNAL
8
(
9
TYPE oracle_loader
10
DEFAULT DIRECTORY ext_data_dir
11
ACCESS PARAMETERS (
12
RECORDS DELIMITED BY NEWLINE
13
LOGFILE ext_data_dir:’comuni_ext.log’
14
FIELDS (
15
Comune
16
17
Provincia
18
CHAR(25)
POSITION(26:27) CHAR(2)
NULLIF Provincia=BLANKS,
19
CAP
POSITION(28:32) CHAR(5)
20
NULLIF CAP=BLANKS,
Codice_ISTAT POSITION(33:36) CHAR(4)
22
NULLIF codice_ISTAT=BLANKS
23
)
24
)
25
LOCATION (‘comuni.txt’)
26
27
POSITION(1:25)
NULLIF Comune=BLANKS,
21
Le prestazioni
varchar2(25),
3
)
PARALLEL;
Creata tabella.
SQL> select count(0) from comuni_esterna
2
;
COUNT(0)
---------8162
SQL>
SQL> select *
2
from comuni_esterna
3
where provincia = ‘NA’
4
and comune like ‘%NAP%’
5
order by cap;
COMUNE
PR CAP
CODI
------------------------- -- ----- ---CASALNUOVO DI NAPOLI
NA 80013 B905
LICIGNANO DI NAPOLI
NA 80015 E577
MARANO DI NAPOLI
NA 80016 E906
MELITO DI NAPOLI
NA 80017 F111
MUGNANO DI NAPOLI
NA 80018 F799
NAPOLI
NA 80100 F839
Selezionate 6 righe.
SQL>
Passati: 00:00:00.00
43
CP 116
F O
C
U
S
Potenza e versatilità dei Database
SQL> select count(0)
2 from comuni_esterna;
COUNT(0)
---------8162
Passati: 00:00:00.02
tati catastrofici: per eseguire il conteggio delle righe
del prodotto cartesiano il DBMS impiega quasi 7
minuti, come si può vedere qui sotto:
SQL> select count(0)
2
from comuni_esterna,
3
comuni_esterna;
COUNT(0)
---------66618244
Passiamo ora ad una query un po’ più complicata da
eseguire
Passati: 00:06:40.09
SQL> select count(0)
2 from comuni
3 where comune like ‘%NAP%’;
Per migliorare le prestazioni delle query che coinvolgono tabelle esterne conviene seguire, laddove
possibile, i consigli riportati in [1].
COUNT(0)
---------7
Conclusioni
Passati: 00:00:01.02
SQL> select count(0)
2 from comuni_esterna
3 where comune like ‘%NAP%’;
COUNT(0)
---------7
Passati: 00:00:03.05
Le due query viste sopra coinvolgono comunque un
basso numero di record. Per fugare i dubbi residui eseguiamo una query che coinvolge il prodotto cartesiano
delle tabelle composto da più di 66 milioni di record:
SQL> select count(0)
2 from comuni, comuni;
COUNT(0)
---------66618244
Passati: 00:00:58.06
SQL> select count(0)
2
from comuni_esterna,
3
comuni_esterna;
COUNT(0)
---------66618244
Le External Table sono uno strumento molto
utile introdotto in Oracle9i per leggere direttamente con una istruzione SQL, dati posti in un
file fuori dal database.
Abbiamo visto come si crea una tabella esterna
e ci siamo rassicurati sulle prestazioni che si ottengono.
Le External Table nascono fondamentalmente come
supporto per l’import dei dati da file esterni.
Una volta definita la tabella esterna è possibile
usare i dati in essa contenuti per caricare tabelle classiche del database mediante un comando INSERT…
SELECT.
Ma ci sono anche altre applicazioni per questo
strumento.
Ad esempio l’utilizzo che ne ha fatto il fantasioso
responsabile CED di cui abbiamo parlato all’inizio di
quest’articolo.
Maggiori dettagli sulle External Table possono
essere trovate nella documentazione e nei siti indicati in bibliografia.
Entrambi i documenti in bibliografia possono essere
trovati sul sito indicato in [3].
Altri articoli sulle nuove funzionalità di Oracle9i
saranno pubblicati sui prossimi numeri di Computer
Programming.
BIBLIOGRAFIA
[1] Oracle – “Oracle9i Database Utilities”, Cap.
11 e 12, Oracle Corp., 2001
[2] Oracle – “Oracle9i Database Administrator’s
guide”, Cap. 15, Oracle Corp., 2001
Passati: 00:00:48.07
44
CP 116
Sulla lettura dei 66 milioni di record la query sulla
tabella esterna è stata addirittura più veloce di quella
sulla tabella classica.
Se si omette la parola chiave PARALLEL in fondo
alla definizione della External Table si ottengono risul-
RIFERIMENTI
[3] http://technet.oracle.com (Oracle Technology Network)