Introduzione al Linguaggio SQL
Flavio Bernardotti (Computer Consulting - Alessandria) SUBLINK: !yachaya!montal!flavio
FIDONET: 2:334/2 - ** PUBLIC DOMAIN SOFTWARE **
Indice
2.3. Operatori matematici di SQL
SQL (pronunciato "sequel") è un linguaggio per la gestione di database molto semplice in quanto legato al metodo di espressione della lingua inglese.
Oltre a questo SQL è abbastanza limitato come numero di vocaboli.
La limitazione del numero delle parole ma allo stesso tempo la strutturazione a linguaggio naturale fa si che le espressioni possibili mediante i suoi vocaboli siano praticamente illimitate.
SQL oltre tutto non si preoccupa dei livelli fisici del database.
In altre parole le gestioni dei file di dati e degli indici diventano completamente trasparenti all'utente.
Come vedremo l'affermazione è solo in parte esatta in quanto SQL può creare indici e file dati.
In ogni caso il metodo di gestione delle relazioni a livello fisico è trasparente.
Al giorno d'oggi la maggior parte dei sistemi di database esistenti sono di tipo relazionale.
Esistono un infinità di prodotti destinati alla creazione di sistemi di questo tipo tra cui citiamo Informix SQL, Oracle e in ultimis DB IV SQL per ambiente MsDos.
In questo fascicolo parleremo di SQL in generale senza fare riferimento a uno specifico prodotto.
Più precisamente l'SQL di cui parleremo è quello definito dall'ANSI e quindi potrebbe risultare che alcuni prodotti posseggano un SQL più ricco.
Vediamo solo di accennare ad alcuni concetti legati alla teoria dei database.
Un approfondimento sulla teoria dei database può essere seguita sui seguenti volumi:
· An Introduction to Database systems (Vol. 1 & 2) di C.J.Date Addison Wesley : ISBN 0-201-19215-2 (vol. 1) ISBN 0-201-14474-3 (vol. 2)
· Manuale delle basi dati di Gio Wiederhold - ETAS Libri
· Database: concetti teorici ed applicativi di S. Misbah Deen - Franco Angeli
· La teoria relazionale dei dati di Atzeni, Batini, De Antonellis - Boringhieri
Un sistema relazionale è un database che viene visto dall'utente come una collezione di Tabelle.
La tabella (table) è l'unità di base del modello relazionale.
Molti sistemi di gestione database come DBIII ci obbligano ad avere una visione dell'insieme molto legato al livello fisico dei files che compongono il database stesso.
In pratica dobbiamo sempre considerare i file .DBF (file dati) e quelli .NDX (file d'indice).
Molti prodotti definiti come relazionali non lo sono di fatto se si considerano al loro livello interno.
Lo diventano nel momento in cui adottano un interfaccia utente SQL.
A livello di linguaggio SQL non dobbiamo preoccuparci come di fatto il sistema informativo tratta i nostri dati a livello fisico.
Possiamo semplicemente decidere di aggiungere uno o più indici per sveltire l'accesso ai dati.
Come farà SQL a utilizzarli possiamo dire che "sono fatti suoi".
In pratica il sistema informativo utilizzato ci permetterà di eseguire una suddivisione tra il livello fisico e quello logico.
Bisogna specificare che SQL è possibile utilizzarlo sia in modo interattivo che da linguaggio.
Vediamo inizialmente alcuni concetti teorici.
Dicevamo precedentemente che il database viene visto come un insieme di Tabelle.
Una tabella non è altro che un insieme di righe e di colonne dove le prime sono i record e le seconde i vari campi che intendiamo trattare.
+-------------------------------------------+
| TABELLA |
+-------+-------+---+-------+-------+-------+
Riga 1 | campo | campo | C | campo | campo | campo |
+-------+-------+ O +-------+-------+-------+
Riga 2 | Mario | Volpe | L | Roma | Asti | 16100 |
+-------+-------+ O +-------+-------+-------+
Riga 3 | campo | campo | N | campo | campo | campo |
+-------+-------+ N +-------+-------+-------+
Riga n | ..... | ..... | A | ..... | ..... | ..... |
+-------+-------+---+-------+-------+-------+
Facciamo subito un esempio.
Supponiamo di avere una tabella che rappresenta i clienti di una ditta XXXXX composta dal numero dell'utente (che deve essere unico), dal nome, dall'indirizzo e dalla città.
TABELLA A
Num. # Nome Indirizzo Citta'
-------------------------------------
A001 Rossi V.Roma Torino
A002 Bianchi V.Trento Milano
A003 Verdi V.Trieste Genova
.... ....... ......... ......
Un' altra tabella potrebbe essere relativa ai prodotti trattati dalla nostra ditta XXXXX, composta anche questa da un numero d'articolo unico, dal nome del prodotto e dal prezzo.
TABELLA B
Num. # Articolo Prezzo
-----------------------------
B001 Chiodo 150
B002 Bullone 300
B003 Dado 120
.... ....... ...
Una terza tabella potrebbe essere quella destinata alla memorizzazione delle vendite.
In pratica risulterà essere una connessione delle due Tabelle precedenti.
TABELLA C
Cliente Articolo Pezzi venduti
----------------------------------
A001 B001 1500
A001 B002 5000
A001 B003 5000
A002 B003 1000
A003 B002 7000
A003 B003 3500
Le prime due Tabelle possono essere definite entità mentre la terza relazione delle prime due.
Senza fare ancora nessun riferimento al linguaggio SQL potremmo definire le Tabelle nel seguente modo.
CREA TABELLA A
( NUM CHAR(5),
NOME CHAR(20),
INDIRIZZO CHAR(20),
CITTA CHAR(20) );
CREA TABELLA B
( ANUM CHAR(5),
ARTICOLO CHAR(30),
PREZZO INTEGER );
CREA TABELLA C
( NUM CHAR(5),
ANUM CHAR(5),
VENDUTI INTEGER );
I dati delle tabelle precedenti potrebbero essere gestite a livello fisico mediante file dati, ed eventualmente indici, differenti. L'implementazione delle Tabelle create come nell'esempio precedente ci mostra come di fatto è possibile creare una descrizione logica del modello in modo indipendente dal suo livello fisico.
Potremmo suddividere i tipi di Tabelle in due classi.
La prima è costituito dalle Tabelle di base (base table) che sono in pratica Tabelle reali esistenti fisicamente come files e come record salvati in questi.
Al contrario il secondo tipo è costituito da Tabelle virtuali che di fatto non esistono a livello fisico ma che si mostrano all'utente come se di fatto esistessero.
Vedremo successivamente di approfondire questi concetti parlando delle Tabelle di base e delle viste (view).
Iniziamo ora a considerare le problematiche legate al query di un sistema di database.
Parleremo successivamente delle operazioni legate alla creazione e alla modifica.
Le funzioni del linguaggio SQL servono all'interrogazione, alla definizione, alla manipolazione e al controllo dei dati in un database relazionale.
Prima di proseguire bisogna anche specificare che SQL è contemporaneamente un linguaggio d'interrogazione per uso interattivo che un vero e proprio linguaggio di programmazione.
Qualsiasi statement SQL che può essere usato in interattivo può essere usato anche in un programma.
Noi inizialmente vedremo SQL dal punto di vista del linguaggio interattivo.
Supponiamo di voler interrogare il database per aver visualizzato tutti i record che tra il campo cognome contengano 'ROSSI'.
Se disponessimo di una macchina capace di comprendere il nostro linguaggio dovremmo comunicargli le nostre intenzioni con una frase del tipo:
· seleziona i campi NOME, COGNOME e INDIRIZZO in CLIENTI
· dove il COGNOME è uguale a 'ROSSI'
In linguaggio SQL la cosa è molto simile.
SELECT NOME, COGNOME, INDIRIZZO FROM CLIENTI
WHERE COGNOME = 'ROSSI';
Come dicevamo inizialmente il linguaggio SQL ci offre delle funzioni adatte alle varie elaborazioni possibili su un sistema di database.
Volendo abbinare lo scopo alle varie funzioni avremmo:
Scopo |
Verbi |
DDL - Data Definition Language |
Create Drop |
DML - Data Manipulation Language |
Insert Update Delete |
DCL - Data Control Language |
Grant |
Queries |
Select |
Tralasciamo momentaneamente la descrizione dei verbi legati al DDL (Data Definition Language), al DML (Data Manipulation Language) e al DCL (Data Control Language) supponendo di avere già un database creato e vediamo subito il verbo, già presentato negli esempi precedenti, legato all'interrogazione. SELECT il primo termine del linguaggio SQL che ora vedremo.
SELECT è il verbo di base che ci permette di eseguire interrogazioni sul sistema di database (query).
Per utilizzare nella forma più generalizzata possibile SELECT è sufficiente conoscere il solo nome della tabella da cui vogliamo estrarre informazioni.
Supponendo di avere una tabella YXZ e volendo vedere tutti i record memorizzati in questa potremmo usare:
SELECT * FROM YXZ;
Tra gli argomenti successivi a SELECT ci sono i nomi dei campi,o per rimanere in tema colonne, che vogliamo visualizzare.
Il carattere * dice di visualizzare indistintamente tutti i campi.
In ogni caso dall'esempio della pagina precedente è facile capire la metodologia per specificare i nomi delle colonne da visualizzare.
SELECT COL1, COL2, .... FROM ....
Il risultato dell'esecuzione di uno statement SELECT è la creazione di una nuova tabella la quale può essere a vita breve, esistente solo sullo schermo del terminale da cui si richiede l'esecuzione, oppure definitiva.
La forma definitiva della tabella si ottiene, come vedremo successivamente, salvandola in modo permanente.
Utilizzando gli esempi appena visti avremmo ottenuto una lista totale di tutti i record indipendentemente dai loro contenuti.
La successiva clausola può essere applicata a SELECT per definire i record che devono essere rintracciati.
WHERE serve a definire il criterio di selezione dei record che devono essere rintracciati da SELECT.
Se avessimo una tabella XYZ del tipo:
NOME DATA IMPORTO
---------------------------
Rossi 12/03/89 120.000
Bianchi 13/03/89 35.000
Verdi 13/03/89 22.000
applicando
SELECT NOME FROM XYZ
WHERE IMPORTO < 100.000;
avremmo come risultato
NOME
-------
Bianchi
Verdi
In pratica diremmo di selezionare dalla tabella XYZ i campi NOME dei record che hanno il campo IMPORTO minore di 100.000.
Le condizioni ammesse sono:
+---------------------+---------+
| Condizione | Simbolo |
+---------------------+---------+
| Uguale | = |
| Parzialmente uguale | LIKE |
| Maggiore | > |
| Minore | < |
| Maggiore o uguale | >= |
| Minore o uguale | <= |
| Diverso | <> |
| Compreso tra | BETWEEN |
| Negazione | NOT |
| Uguale a uno della | |
| lista (*) | IN |
+---------------------+---------+
Le varie condizioni usate con WHERE possono essere collegate tra loro mediante operatori logici. In altre parole potremmo creare costrutti del tipo:
SELECT NOME FROM XYZ
WHERE IMPORTO < 100.000 AND IMPORTO > 30.000;
oppure
SELECT NOME FROM XYZ
WHERE NOME < 'Mario’ AND IMPORTO >= 100.000;
e ancora
SELECT NOME, COGN, INDIR FROM XYZ
WHERE PROVINCIA = 'Alessandria’ OR DATA < '02-JAN-89';
È possibile combinare clausole OR con quelle di AND utilizzando anche anche le parentesi rotonde contemplate dal linguaggio SQL.
SELECT NOME, COGN, INDIR FROM XYZ
WHERE (CITTA = 'Asti' AND IMPORTO < 100.000) OR CITTA = 'Pavia’;
Penso che non ci sia da aggiungere altro per quanto riguarda gli operatori per imbastire le condizioni quali <, >, >= ecc.
Alcune spiegazioni devono essere riportate invece sulle condizioni tipo LIKE, BETWEEN e IN.
Procediamo con ordine.
Mediante la specifica LIKE è possibile creare costrutti del tipo:
SELECT NOME, COGN FROM XYZ
WHERE COGN LIKE 'Rossi';
Utilizzare la LIKE come nell'esempio precedente è come fare
SELECT NOME, COGN FROM XYZ
WHERE COGN = 'Rossi';
La vera potenza di LIKE sta nella possibilità di utilizzare due tipi di caratteri jolly per testare delle uguaglianze parziali.
I due caratteri sono precisamente '%' (percento) e '_' (underscore).
Il primo può valere uno o più caratteri mentre il secondo può sostituirne uno solo.
Supponendo di avere una tabella con:
+---------------------+---------+
| Condizione | Simbolo |
+---------------------+---------+
| Uguale | = |
| Parzialmente uguale | LIKE |
| Maggiore | > |
| Minore | < |
| Maggiore o uguale | >= |
| Minore o uguale | <= |
| Diverso | <> |
| Compreso tra | BETWEEN |
| Negazione | NOT |
| Uguale a uno della | |
| lista (*) | IN |
+---------------------+---------+
SELECT DITTA FROM XYZ
WHERE DITTA LIKE '%ogn%';
In questo caso avremmo visualizzato
DITTA
---------
Bolognini
Bolognesi
Bologna
Bognardi
Il comando
SELECT DITTA FROM XYZ
WHERE DITTA LIKE '_ogn%';
visualizzerebbe
DITTA
---------
Bognardi
e ancora
SELECT DITTA FROM XYZ
WHERE DITTA LIKE '%ogn___';
darebbe come risultato
DITTA
---------
Bolognini
Bolognesi
Sono anche possibili costrutti del tipo
SELECT DITTA FROM XYZ WHERE DITTA LIKE 'B%';
SELECT DITTA FROM XYZ WHERE DITTA LIKE '____';
Potrebbe verificarsi il caso in cui i caratteri '_' o '%' facciano parte di quello che si vuole ricercare.
Prendete ad esempio una tabella del tipo:
FLAGS FUNCTION
----------------------
O_RDONLY open
O_RDWR open
P_WAIT spawn
P_NOWAIT spawn
Per fare in modo che il carattere '_' non venga interpretato come carattere jolly bisogna farlo precedere da un carattere d'escape.
SQL definito dall'ANSI dispone della specifica ESCAPE da unire con LIKE per stabilire il carattere d'escape.
Ad esempio
SELECT * FROM XYZ WHERE FLAGS LIKE 'O\_R%' ESCAPE '\';
In questo caso '\' viene interpretato da SQL come carattere d'escape e '_' come carattere effettivo da ricercare e non come carattere jolly.
Un altro operatore relazionale atipico (rispetto a <, > ecc.) è IN.
Mediante IN è possibile specificare una lista di valori da utilizzare come clausole di ricerca.
Un costrutto del tipo:
SELECT * FROM XYZ WHERE DITTA IN ('Ansaldo’,'Montedison');
selezionerà tutte le righe in cui la colonna DITTA risulterà essere nella lista (nell'esempio solo Ansaldo o Montedison).
Potremmo ad esempio disporre di una tabella del tipo:
CLIENTE IMPORTO
------------------------
Rossini 50.000
Bolognini 30.000
Rossi 5.000
Verdi 90.000
Se scrivessimo
SELECT CLIENTE FROM XYZ WHERE IMPORTO IN (5000,30000,90000);
avremmo come risultato
CLIENTE
----------
Rossi
Bolognini
Verdi
L'ultima condizione è BETWEEN (compreso tra .... e ....).
Il comando
SELECT NOME FROM XYZ WHERE PREZZO BETWEEN 50000 AND 100000;
è equivalente a scrivere
SELECT NOME FROM XYZ WHERE PREZZO >= 50000 AND PREZZO <= 100000;
Per definizione si assume che i valori specificati con BETWEEN siano in ordine ascendente.
Vediamo infine la negazione NOT che funziona in congiunzione con
le precedenti condizioni LIKE, BETWEEN e IN.
Ad esempio
SELECT * FROM XYZ WHERE IMPORTO NOT BETWEEN 3000 AND 10000;
visualizzerebbe tutte le righe il cui contenuto del campo IMPORTO non è compreso tra 3000 e 10000.
Allo stesso modo
SELECT * FROM XYZ WHERE NOME NOT LIKE 'Rossi';
mostrerebbe tutte le righe con contenuto del campo NOME diverse da 'Rossi'.
Alcuni campi di un record potrebbero valere NULL.
NULL può essere utilizzato come termine di selezione.
Ad esempio
SELECT * FROM XYZ WHERE NOME IS NULL;
seleziona tutti i record che hanno NULL come campo NOME.
L'opposto è
SELECT * FROM XYZ WHERE NOME IS NOT NULL;
Nel linguaggio SQL esistono alcune specifiche utilizzabili con SELECT atte a ordinare i dati che devono essere mostrati e a raggrupparli.
SELECT infatti visualizzerebbe le informazioni nello stesso ordine con cui sono rintracciate.
La specifica
ORDER BY [COLONNA] [ASC|DESC]
permette di ordinare i dati su una determinata colonna in modo ascendente (ASC) o discendente (DESC).
Disponendo di una tabella:
NOME CITTA
-----------------
Rossi Alba
Bianchi Asti
Verdi Cuneo
Alessi Milano
ed eseguendo
SELECT NOME FROM XYZ ORDER BY NOME ASC;
avremmo come risultato
NOME
-------
Alessi
Bianchi
Rossi
Verdi
mentre l'ordine sarebbe inverso nel caso di
SELECT NOME FROM XYZ ORDER BY NOME DESC;
Il comando d'ordinamento non altera fisicamente le Tabelle ma influisce semplicemente sulla visualizzazione.
Vedremo l'utilizzo della specifica GROUP BY nel prossimo capitolo dopo aver introdotto gli operatori matematici.
Il linguaggio SQL, pur non essendo orientato al calcolo matematico, dispone della possibilità di implementazione dei normali operatori matematici utilizzabili con lo statement SELECT per calcoli sui campi.
Disponendo di una tabella del tipo:
CLIENTE IMPORTO IVA
-----------------------------
Rossi 100000 19000
Verdi 120000 22800
Bianchi 180000 34200
e impostando
SELECT CLIENTE, (IMPORTO + IVA) FROM XYZ;
avremo come risultato
CLIENTE (IMPORTO + IVA)
---------------------------
Rossi 119000
Verdi 142800
Bianchi 214200
In ogni caso gli operatori SQL sono i seguenti.
Addizione +
Sotrazione -
Moltiplicazione *
Divisione /
Conteggio COUNT
Massimo MAX
Minimo MIN
Media AVG
Somma SUM
Gli operatori matematici dovrebbero essere intuitivi.
I seguenti costrutti valgono ad esempio per l'uso di questi.
SELECT CLIENTE, (IMPORTO / 2) FROM XYZ;
SELECT CLIENTE, ((IMPORTO + IVA) * 2) FROM XYZ;
Vediamo ora gli altri operatori.
Il primo è COUNT.
In pratica questo riporta il numero degli header rintracciati da SELECT.
Tenendo per valida la tabella precedente contenente tre righe e eseguendo
SELECT COUNT(CLIENTE) FROM XYZ;
otterremmo 3.
Potremmo anche utilizzare COUNT con WHERE per sapere quante righe sono presenti:
SELECT COUNT(*) FROM XYZ;
WHERE è applicabile anche con SELECT COUNT().
Se volessimo, ad esempio, sapere quanti clienti hanno speso più di 150.000 potremmo utilizzare:
SELECT COUNT(CLIENTE) FROM XYZ WHERE IMPORTO > 150000;
Altri due operatori destinati a selezionare i valori massimi e minimi contenuti nei campi specificati sono MAX e MIN.
Supponendo di voler trovare l'importo minore nella tabella dei clienti:
SELECT MIN(IMPORTO) FROM XYZ;
È possibile usare come argomento degli operatori MIN e MAX anche dei calcoli matematici.
SELECT MAX(IMPORTO / 100) FROM XYZ;
L'operatore SUM è applicabile solo a campi numerici e restituisce la somma dei valori contenuti in questi.
SELECT SUM(IMPORTO) FROM XYZ;
restituisce la somma di tutti gli importi presenti nella tabella (sempre quella riportata ad esempio nelle pagine precedenti).
Se dovessimo calcolare una media di tutti gli importi presenti nella tabella potremmo dare il comando:
SELECT (SUM(IMPORTO) / COUNT(IMPORTO)) FROM XYZ;
In pratica verrebbe calcolata la somma di tutti gli importi presenti nella tabella e successivamente divisa per il numero delle righe conteggiate.
Il linguaggio SQL possiede un operatore particolare che permette di calcolare la media senza dover eseguire calcoli come nell'esempio precedente.
L'operatore è AVG.
Usandolo l'esempio precedente diventerebbe :
SELECT AVG(IMPORTO) FROM XYZ;
Esiste un’ulteriore specifica utilizzabile con l'operatore COUNT è DISTINCT.
Una determinata colonna potrebbe contenere dei valori duplicati.
Se si desidera avere il conteggio non considerando i duplicati è possibile creare costrutti del tipo:
SELECT COUNT(DISTINCT CLIENTE) FROM XYZ;
Supponiamo di avere una tabella XYZ contenente l'elenco delle vendite di un determinato anno.
CLIENTE IMPORTO DATA
-------------------------------
Rossi 10000 12/12/89
Bianchi 20000 12/12/89
Verdi 12000 12/12/89
Rossi 30000 13/12/89
Rossi 34000 15/12/89
Mediante
SELECT COUNT(CLIENTE) FROM XYZ;
avremmo come risultato il numero delle righe (record) presenti nella tabella ovvero 5.
Desiderando sapere il numero delle ditte a cui è stato venduto del materiale dovremmo dare:
SELECT COUNT(DISTINCT CLIENTE) FROM XYZ;
Avremmo come risultato 3.
Altre tre funzioni di utilità non elencate tra gli operatori precedenti sono LOWER, UPPER e LENGTH.
Il contenuto delle righe ricercate potrebbe essere vario.
Il nome di una ditta potrebbe risultare come "Rossi", come "ROSSI" o ancora come "rossi".
Se avessimo una tabella con:
CLIENTE IMPORTO
------------------
Rossi 100000
Verdi 120000
Bianchi 130000
e cercassimo di rintracciare qualche riga con
SELECT CLIENTE FROM XYZ WHERE CLIENTE = 'ROSSI';
non troveremmo nulla dato che il linguaggio fa differenza tra caratteri maiuscoli e minuscoli e quindi "Rossi" risulta essere differente da "ROSSI".
Le funzioni LOWER e UPPER ci permettono di convertire i valori delle colonne rintracciate da SELECT in minuscolo e in maiuscolo. Non avendo la sicurezza sul tipo di caratteri contenuti in un campo potremmo usare:
SELECT UPPER(CLIENTE) FROM XYZ WHERE CLIENTE = 'ROSSI';
oppure
SELECT LOWER(CLIENTE) FROM XYZ WHERE CLIENTE = 'rossi';
La funzione LENGTH restituisce invece la lunghezza di ogni riga.
SELECT LENGTH(CLIENTE) FROM XYZ;
In pratica conta i caratteri di ogni campo.
Nel capitolo precedente avevamo rimandato il discorso legato all'utilizzo della specifica GROUP BY.
Per introdurre il concetto di raggruppamento possiamo vedere un esempio pratico.
Supponiamo di avere una tabella del tipo:
PRODOTTO TIPO QUANTITA
---------------------------
Chiodi 1 20000
Bulloni 1 12000
Dadi 1 17000
Chiodi 2 10000
Se desiderassimo conoscere il numero totale dei pezzi presenti indipendentemente dal tipo del prodotto potremmo usare:
SELECT SUM(QUANTITA) FROM XYZ;
Nel caso in cui ci interessasse sapere quanti chiodi abbiamo possiamo usare:
SELECT PRODOTTO, SUM(QUANTITA) FROM XYZ WHERE PRODOTTO='Chiodi';
Se volessimo avere visualizzate tutte le somme dei pezzi raggruppati per tipo, allora:
SELECT PRODOTTO, SUM(QUANTITA) FROM XYZ GROUP BY PRODOTTO;
Il risultato sarebbe
PRODOTTO SUM(QUANTITA)
------------------------
Chiodi 30000
Bulloni 12000
Dadi 17000
Parlando del WHERE avevamo visto come questo servisse ad eliminare dalla selezione le righe.
WHERE non è applicabile a GROUP BY.
Nel caso in cui si vuole eliminare alcuni gruppi è possibile utilizzare la specifica HAVING.
È possibile ad esempio creare costrutti del tipo:
SELECT CAMPO1 FROM XYZ GROUP BY CAMPO2 HAVING COUNT(*) > 1;
Fino a questo punto abbiamo visto come ricercare dati da una determintata tabella.
In altre parole abbiamo considerato una tabella come un archivio in cui sono memorizzati dei dati e mediante SELECT abbiamo estratto i dati che ci interessavano.
Un sistema di database relazionale viene considerato come una raccolta di Tabelle e quindi la visione che abbiamo deve essere espansa.
I concetti generali li avevamo accennati inizialmente.
Utilizzando lo statement SELECT abbiamo specificato il nome delle colonne scrivendo semplicemente il suo nome non preoccupandoci invece di definire la tabella in cui queste erano contenute in quanto, di fatto, gli esempi erano sempre e solo relativi a una singola tabella.
Un determinato nome di colonna potrebbe essere comune a più Tabelle.
Nel caso in cui SELECT deve ricercare dati da più Tabelle è chiaro che se specificassimo solo il nome di un campo potrebbe trovarsi nei pasticci, proprio per il fatto che la colonna potrebbe essere definita in più Tabelle.
SQL al fine di evitare questo tipo di problemi accetta la specifica del nome di colonna preceduto da nome della tabella in cui si trova questo.
Al fine di portare un esempio a quanto detto prima possiamo supporre di avere le seguenti due Tabelle.
TABELLA AAA NOME INDIRIZZO
----------------
Rossi V.Roma
..... ......
TABELLA BBB NOME IMPORTO
----------------
Rossi 100000
..... ......
Volendo eseguire un costrutto con SELECT potremmo scrivere:
SELECT AAA.NOME, BBB.NOME FROM AAA, BBB;
Tutti gli statement in cui si richiedono colonne da due o più Tabelle vengono chiamati JOIN.
In pratica gli operatori JOIN sono quelli che distinguono un database relazionale da uno non relazionale.
La sintassi di quanto detto prima è:
SELECT tabella1.campo, tabella2.campo ... FROM tabella1,tabella2;
L'esempio precedente avrebbe creato come risultato la visualizzazione di tutte le combinazioni possibili tra il campo NOME di AAA e quello NOME di BBB.
La specifica WHERE è utilizzabile anche in questo caso al fine delle righe dalla visualizzazione.
È possibile creare costrutti del tipo:
SELECT AAA.*, BBB.* FROM AAA, BBB WHERE AAA.NOME = BBB.NOME;
oppure utilizzando anche più Tabelle
SELECT A.CITTA, B.NOME, C.IMPORTO FROM A, B, C;
SQL permette di assegnare un nuovo nome alle Tabelle di un determinato statement.
L'operazione è normalmente conosciuta come ALIAS.
Una tabella ALIAS rimane attiva solo durante l'esecuzione dello statement.
Lo standard ANSI riconosce ALIAS con il nome di correlazione.
SELECT A.*,B.* FROM TABELLA1 A, TABELLA2 B WHERE A.CAMPO = B.CAMPO;
Assegna come ALIAS il nome A alla TABELLA1 e B alla TABELLA2.
Per introdurre il concetto rivediamo l'utilizzo della specifica IN.
Avevamo detto che questo controlla se un valore è presente in una lista.
In altre parole il predicato IN formulato con:
campo IN (a, b, c, ...);
è equivalente a
campo = a OR campo = b OR campo = c OR ... ;
I valori della lista potrebbero essere quelli selezionati da uno statement SELECT e cioè potremmo fare una sottointerrogazione per ricavare i dati della lista che ci interessano.
Ad esempio potremmo usare:
SELECT NOME,INDIRIZZO FROM XYZ WHERE NOME IN (SELECT NOME FROM ZYX WHERE CITTA = 'Asti');
Il SELECT tra parentesi selezionerebbe tutti i campi NOME della tabella ZYX che possiedono come città 'Asti'.
Il primo SELECT visualizzerebbe i campi NOME e INDIRIZZO di tutte le righe il cui NOME è presente nella lista creata dal costrutto precedente.
Sono anche possibili statement del tipo:
SELECT NOME, INDIRIZZO FROM TABELLA1 WHERE CITTA IN
(SELECT CITTA FROM TABELLA2 WHERE NUMERO IN
(SELECT NUMERO FROM TABELLA3 WHERE IMPORTO > 50000));
oppure
SELECT NOME FROM TABELLA1 WHERE CITTA = 'Asti' AND IMPORTO IN
(SELECT IMPORTO FROM TABELLA2 WHERE IMPORTO > 50000);
Come avete sicuramente notato negli esempi precedenti è stata utilizzata la specifica IN.
Se utente sa che una determinata sotto-interrogazione ritorna esattamente un solo valore può utilizzare un normale operatore di confronto (=, >, <, ecc.).
Ad esempio:
SELECT NOME FROM TABELLA1 WHERE CITTA =
(SELECT CITTA FROM TABELLA2 WHERE NOME = 'Rossi');
oppure
SELECT NOME FROM TABELLA1 WHERE IMPORTO <
(SELECT MAX(IMPORTO) FROM TABELLA2);
Terminiamo questa panoramica sulle interrogazioni guardando ancora il concetto di unione.
Due interrogazioni con SELECT possono essere unite al fine di creare una tabella unica.
Il vocabolo che permette di eseguire questa funzione è UNION la cui sintassi è:
SELECT .... FROM tabella1 UNION SELECT .... FROM tabella2;
Il seguente esempio crea una tabella che è la somma delle due create dai comandi SELECT.
SELECT * FROM TABELLA1 UNION SELECT * FROM TABELLA2;
Nel caso che si vogliano eseguire più di due interrogazioni è necessario utilizzare le parentesi rotonde per la suddivisione.
Un esempio:
SELECT NOME FROM TABELLA1 UNION
(SELECT NOME FROM TABELLA2 UNION SELECT INDIRIZZO FROM TABELLA3);
Con questo abbiamo concluso il discorso legato alle interrogazioni.
Avremmo potuto iniziare la trattazione di SQL partendo dalla creazione, cancellazione e modifica dei records in un sistema di database.
Ho preferito iniziare dalle interrogazioni lasciando la parte relativa alle modifiche, creazione inclusa, per ultima.
In questo modo chi legge questo fascicolo incomincia a chiarirsi le idee manipolando oggetti già costruiti senza dovere, inizialmente, sforzarsi a immaginarsi come possono essere creati questi oggetti.
Proseguiremo ora con alcuni argomenti legati all'utilizzo dei prodotti già creati.
Inizialmente parlando di Tabelle avevamo fatto una distinzione.
In pratica avevamo detto che potevamo classificare queste in due classi differenti: le TABELLE di BASE e le TABELLE VIRTUALI.
Le prime sono quelle che esistono realmente e che quindi occupano uno spazio fisico di memoria.
Queste vengono create con il comando SQL, visto in un esempio iniziale e discusso successivamente, CREATE TABLE.
Il secondo tipo di Tabelle in realtà non esistono fisicamente ma soltanto virtualmente e quindi non occupano spazio nel database.
Questo tipo di Tabelle vengono definite in SQL come VIEWS (viste) e appaiono all'utente come se fossero reali.
È possibile creare Tabelle di questo tipo con determinate condizioni, ad esempio per fare in modo che l'utente veda solo nella tabella tutti i record di quelli che abitano in una certa città.
Una VIEW può essere creata con le parole CREATE VIEW.
Il contenuto di una VIEW viene scritta da uno statement SELECT utilizzato nel seguente modo:
CREATE VIEW PROVA AS SELECT .... ;
Fate attenzione che il nome della VIEW non sia in conflitto con quello di qualche tabella reale.
Le colonne di una VIEW possono essere specificati oppure copiati mediante i campi specificati in SELECT.
Prendiamo ad esempio:
CREATE VIEW PROVA AS SELECT * FROM TABELLA1;
Lo statement crea una tabella virtuale, VIEW, chiamata PROVA contenente tutte le colonne (vedi '*' di SELECT) contenute nella TABELLA1.
CREATE VIEW PROVA ( NM, IMP ) AS
SELECT NOME, IMPORTO FROM TABELLA1;
Il precedente costrutto invece definisce la struttura della VIEW contenente i campi NM e IMP corrispondenti con quelli NOME e IMPORTO della tabella di base TABELLA1.
I nomi delle colonne di una VIEW devono essere specificati in modo esplicito nel caso in cui nello statement SELECT ci sia una funzione di tipo matematico oppure SUM, MAX ecc.
Ad esempio:
CREATE VIEW PROVA (NM, TOTIMP) AS
SELECT NOME, SUM(IMPORTO) FROM TABELLA1;
Un altro esempio potrebbe essere
CREATE VIEW PROVA (NM, TOTIMP) AS
SELECT NOME, (IMPORTO + IVA) FROM TABELLA;
Sono anche possibili costrutti del tipo:
CREATE VIEW PROVA AS
SELECT NOME, IMPORTO FROM TABELLA1 WHERE CITTA = 'Asti';
Ricordandosi quanto detto a riguardo della natura virtuale della VIEW possiamo analizzare l'esempio precedente per fare alcune considerazioni aggiuntive.
Quando CREATE VIEW viene eseguito lo statement SELECT-FROM-WHERE non è di fatto eseguito ma semplicemente salvato nel catalogo.
L'utente in ogni caso vede la VIEW creata come se fosse una tabella reale.
In questo caso disporrà di una tabella PROVA che può essere considerata come una finestra nella tabella reale dalla quale la VIEW deriva.
Le operazioni di update della view sono riflesse sulla tabella originale anche se di fatto bisogna tenere a mente che non sempre le VIEW sono updatabili.
Vedremo successivamente il perchè di questa affermazione dopo aver visto le operazioni di INSERT, UPDATE ecc.
L'utilità di una VIEW è legata ad alcuni aspetti quali ad esempio la possibilità di mostrare gli stessi dati ad utenti in modo differente oltre ad alcune metodologie utilizzate per la ristrutturazione dei database.
La rimozione di una vista può essere eseguita semplicemente utilizzando il comando:
DROP VIEW NOMEVIEW;
Fino a questo punto abbiamo visto soltanto la parte di linguaggio SQL adatto all'interrogazione.
Lavorando su database spesso ci si ritrova nella necessità di apportare modifiche ai contenuti dei records.
SQL dispone del verbo UPDATE che ci permette di svolgere questo tipo di azioni.
La sintassi generalizzata è:
UPDATE TABELLA SET .... ;
Un comando del tipo
UPDATE TABELLA1 SET NOME = 'Rossi';
setterebbe tutti i record della TABELLA1 inserendo nella colonna NOME il nome 'Rossi'.
Update può essere in questo caso veramente pericoloso dato che ha la possibilità di cambiare tutti i record di una tabella.
La clausola WHERE può restringere il campo dei dei record su cui agire.
Ad esempio
UPDATE TABELLA1 SET NOME = 'Rossi' WHERE NOME = 'Bianchi';
rintraccerebbe le righe della tabella in cui il campo NOME contiene 'Bianchi' e sostituirebbe questo con 'Rossi'.
Supponiamo di avere un database della SIP in cui vengono memorizzati i dati relativi alla rete telefonica.
Una tabella potrebbe essere:
TABELLA NUMERI
NUMERO ZONA ARMADIO
-----------------------------
223456 131 A-123
223457 131 A-123
223459 131 A-123
223462 131 A-123
985685 131 A-123
985686 131 A-123
Supponiamo a questo punto che i numeri inizianti per 22.. debbano essere aggiornati seconda la nuova numerazione.
In pratica dovrebbero avere un incremento di 762231 per fare in modo che il numero 223456 diventi 985687 e cosi via.
Il comando per l'update potrebbe essere:
UPDATE NUMERI
SET NUMERO = NUMERO + 762231
WHERE NUMERO > 223455 AND NUMERO < 223463;
Mediante UPDATE non è possibile cambiare records da più di una tabella per volta.
Includo tra i verbi adatti a modificare un record anche quelli legati all'inserimento e alla cancellazione.
Il verbo SQL che permette di creare un nuovo record è INSERT la cui sintassi generalizzata è:
INSERT INTO TABELLA .... ;
I valori da inserire devono essere specificati tra parentesi dopo la parola VALUE.
Guardate il seguente esempio:
INSERT INTO TABELLA VALUE(1,'Rossi','V.Roma’,'Asti',100000);
Utilizzando il metodo appena descritto dovremo assegnare tutti i campi del record che intendiamo inserire.
Nel caso in cui volessimo solo assegnare alcuni campi dovremmo specificare questi prima di utilizzare la parola VALUE.
Prendiamo l'esempio precedente e consideriamolo un’assegnazione di tutti i campi.
Questi potrebbero essere:
NUMERO, NOME, INDIRIZZO, CITTA, IMPORTO
Se volessimo solo assegnare il campo nome e quello relativo all'importo potremmo fare:
INSERT INTO TABELLA (NOME, IMPORTO) VALUES ('Rossi',100000);
Il valore NULL può essere utilizzato come valore.
In ogni caso l'assegnazione può anche non essere diretta ma frutto di un’interrogazione eseguita mediante SELECT a patto che questa non sia eseguita sulla stessa tabella in cui si vuole inserire il valore.
C'è da notare che usando la specifica VALUE si crea un solo record mentre utilizzando SELECT viene creato un record per ogni valore restituito.
Vediamo alcuni esempi in pratica:
INSERT INTO TABELLA1 (NOME, IMPORTO)
SELECT NOME, IMPORTO FROM TABELLA2;
oppure anche con WHERE:
INSERT INTO TABELLA1 (NOME, IMPORTO)
SELECT NOME, IMPORTO FROM TABELLA2 WHERE CITTA = 'Romà;
La cancellazione di un record avviene mediante DELETE.
DELETE FROM TABELLA;
cancella tutti i record della tabella.
Nel caso in cui si vogliono selezionare i records da cancellare bisogna utilizzare la parola WHERE.
DELETE FROM TABELLA WHERE NOME = 'Rossi' AND CITTA = 'Roma’;
Dopo aver visto le metodologie per eseguire interrogazioni sulle Tabelle diamo un occhiata finalmente al modo di crearle.
Penso che a questo punto sia abbastanza chiaro che cosa si intende per tabella.
La creazione avviene con la parola CREATE usata nel seguente modo:
CREATE TABLE (CAMPO1, CAMPO2, ....);
Le specifiche tra parentesi sono in pratica i nomi dei campi che si vogliono avere nella tabella seguiti dalla specifica del tipo che tra poco vedremo.
I nomi dei campi non possono essere parole riservate di SQL.
Prima di vedere la tipologia dei campi facciamo un esempio.
CREATE TABLE (CAMPO1 CHAR(20), CAMPO2 NUMBER(5));
Vediamo i tipi validi.
Voglio premettere che la seguente tipologia è quella di Informix SQL e che quindi in altri prodotti potrebbe essere più ridotta o più espansa.
In ogni caso consiglio la consultazione del manuale del prodotto specifico che utilizzate.
TIPO NOTE
----------------------------------------------------------------
CHAR(n) Una stringa di caratteri lunga n.
n deve essere >= 1 e <= 32.767
SMALLINT Un numero tra -32.767 e +32.767
INTEGER Un numero tra -2.147.483.647 e +2.147.483.647
DECIMAL[(m[,n])] Un numero in virgola mobile con m valori
significativi (m <= 32) e con n valori
decimali.
SMALLFLOAT Un numero in virgola mobile corrispondente al
tipo float del linguaggio C
FLOAT Un numero in virgola mobile corrispondente al
tipo double del linguaggio C
MONEY[(m[,n])] Un numero decimale che indica una cifra di
soldi visualizzata con il simbolo $.
SERIAL[(n)] Un numero sequenziale assegnato
automaticamente da SQL.
DATE Una
In alcuni casi si desidera che un determinato campo non possa assumere il valore NULL.
Se si desidera che SQL richieda in modo obbligatorio 'inserimento di un campo e' possibile specificare a seguito del tipo la parola NOT NULL.
Nel caso in cui avviene questa specifica SQL segnalerà un messaggio d'errore tutte le volte che si cercherà di omettere l'inserimento.
Un esempio:
CREATE TABLE TABELLA (
IDNUM CHAR(5) NOT NULL,
NOME CHAR(20),
INDIR CHAR(20),
IMPORTO INTEGER);
L' assegnazione al campo IDNUM è obbligatorio.
Se si desidera che il valore di una determinata colonna sia unico in tutta la tabella è possibile specificare la word UNIQUE.
Fate attenzione che UNIQUE può essere specificato solo con quei campi che sono anche stati definiti come NOT NULL.
Riprendendo l'esempio precedente:
CREATE TABLE TABELLA (
IDNUM CHAR(5) NOT NULL UNIQUE,
...... .........
);
Nel caso in cui i campi da definire come UNIQUE siano più di uno è possibile eseguire la specifica anche in fondo alla dichiarazione.
Prendiamo il seguente esempio:
CREATE TABLE TABELLA (
IDNUM CHAR(5) NOT NULL UNIQUE,
DITTA CHAR(20) NOT NULL UNIQUE,
PIVA CHAR(11) NOT NULL UNIQUE
);
potrebbe essere definita anche come:
CREATE TABLE TABELLA (
IDNUM CHAR(5) NOT NULL,
DITTA CHAR(20) NOT NULL,
PIVA CHAR(11) NOT NULL,
UNIQUE (IDNUM,DITTA,PIVA)
);
È possibile eliminare una tabella mediante
DROP TABLE NOMTAV;
Quando viene creata una tabella e successivamente si esegue un inserimento i dati vengono accodati a quelli già presenti senza che venga dato un preciso ordine.
Se i records sono molti la ricerca e l'accesso ai dati può diventare estremamente lento.
Per sveltire le operazioni di interrogazione possono essere creati e utilizzati gli indici.
Se si richiede la creazione di un indice SQL esegue la copia, questa volta ordinandoli, dei dati in un altro file (indice).
La copia dei dati non è completa ma soltanto dei campi o delle parti di questi specificati come chiavi per la ricerca.
A questi vengono associati dei puntatori al file di dati in modo tale che se si richiede una ricerca SQL utilizza il nuovo file e mediante questi rintraccia l'ubicazione precisa del record nel file principale.
Il trattamento degli indici avviene mediante un algoritmo di B-Tree di cui non discuteremo in questo testo.
SQL è in grado di accorgersi automaticamente dell'esistenza di un determinato indice e quindi di utilizzare questo per il rintracciamento dei dati al posto della ricerca sequenziale nel file .DAT.
Come avevamo detto precedentemente l'uso degli indici da parte di SQL può essere completamente trasparente all'utente.
Molti prodotti come INFORMIX SQL richiedono, durante la procedura interattiva di creazione delle Tabelle, se i vari campi devono possedere un indice associato e se questo può contenere chiavi duplicate. La creazione da interprete di un indice può avvenire mediante:
CREATE INDEX NOMIND ON TABELLA(CAMPO1, ....);
Se disponessimo di una tabella TABELLA1 con i seguenti campi
NOME, INDIRIZZO, PIVA, IMPORTO
potremmo, ad esempio, creare un indice utilizzando come chiave NOME+PIVA.
CREATE INDEX NEWINDEX ON TABELLA1(NOME, PIVA);
Se avessimo scelto di creare un indice in modo che in questo non possano risultare dupplicazioni avremmo dovuto utilizzare la parola SQL UNIQUE nel seguente modo:
CREATE UNIQUE INDEX NEWINDEX ON TABELLA1(NOME, PIVA);
L'eliminazione di un indice si ottiene mediante
DROP INDEX NOMIND;
Quando si accede a una macchina fornita di un sistema operativo multiutente bisogna eseguire la procedura di login durante la quale l'utente viene identificato con un nome associato a quell'accesso.
Se si dispone di un sistema di database installato su una macchina multiutente esiste quasi sicuramente la possibilità che più persone possano lavorare vicino a questo, magari anche contemporaneamente.
Chiaramente, in questo caso, si deve regolamentare i permessi d'accesso alle Tabelle per fare in modo che ogni persona faccia esattamente quello che deve, o che può, fare senza correre il pericolo che avvengano modifiche o operazioni non gradite sulle Tabelle da parte di persone estranee o non autorizzate.
Colui che crea un certa tabella può stabilire quali operazioni possono essere eseguite. SQL dispone della funzione GRANT destinata a questa operazione.
La sintassi generalizzata è:
GRANT lista[ALL] ON tabella TO lista[PUBLIC];
Ad esempio:
GRANT ALL ON TABELLA1 TO PUBLIC;
permette a chiunque di eseguire qualsiasi statement sulla TABELLA1.
GRANT INSERT, SELECT ON TABELLA1 TO MARIO;
permette l'inserimento e il query all'utente identificato come Mario.
GRANT UPDATE(NOME,IMPORTO) ON TABELLA1 TO MARIO;
permette a Mario di modificare i campi NOME e IMPORTO di TABELLA1.
GRANT ALL ON TABELLA1 TO MARIO,LUCY,FRANCO;
permette l'esecuzione di tutti gli statement a Mario, Lucy e a Franco.
La revoca dei permessi può essere eseguita con il verbo REVOKE.
Ad esempio:
REVOKE ALL ON TABELLA1 FROM MARIO;
rimuove tutti i diritti a Mario.
REVOKE INSERT ON TABELLA1 FROM MARIO;
rimuove il permesso d'inserimento a Mario.
Con questo concludo questo breve fascicolo tralasciando tutta il trattamento dei report, dei form ecc.
Oltre tutto in questa parte è stata omessa la parte legata alla programmazione con SQL.
Tutto questo è presente nella seconda parte di questo testo che verrà utilizzato come dispensa per un corso che dovrò tenere tra circa due mesi e che quindi non è ancora possibile lasciare tra il materiale PUBLIC DOMAIN.
In ogni caso, come già detto precedentemente, le informazioni qui riportate sono generalizzate e non si attengono a qualche prodotto specifico anche se in alcuni casi, ad esempio parlando della tipologia dei dati, mi sono legato a Informix SQL.
La seconda parte del fascicolo è invece volutamente legata a questo prodotto del quale si guarderanno anche altri aspetti.
Data la scarsa documentazione esistente in italiano su SQL penso che il breve testo sia potuto servire a coloro che desideravano introdursi al fine di cercare di capire quali sono le possibilità di un linguaggio come questo.
(C) Copyright 1989 Flavio Bernardotti - Montecastello
Tel. 0131-355506 (Dati) 0131-355529 (Voce)
Via Trento, 10 - 15040 Montecastello (Al.)
SQL è un marchio registrato dell' I.B.M. Inc.
INFORMIX è un marchio registrato dell' INFORMIX Software Inc.
Nota non relativa al Linguaggio SQL
Il seguente testo è da considerarsi PUBBLICO DOMINIO per scopi non commerciali.
--------------------------------------------------------------------------------------------------------
Non è assolutamente permesso a ditte che si occupano di formazione professionale di utilizzare questo testo per fornire dispense nei corsi da loro tenuti.
Non è neppure permesso copiare, anche solo parzialmente, il testo.
--------------------------------------------------------------------------------------------------------
Voglio sottolineare queste condizioni in quanto con testi da me scritti precedentemente si è verificata la sostituzione del copyright originale con quello delle ditte che li utilizzavano per i fatti loro.
Purtroppo chi è vicino all'informatica per passione, anche a livello amatoriale, quasi sempre non immagina che il mondo professionale è spesso costituito da molti "mangia-mangia" (per fortuna non tutti) che non si fanno scrupoli quando si tratta di prendere un programma e di sostituirgli il copyright per poi rivenderlo come farina del loro sacco.
Lo spirito filosofico che sostiene il PUBLIC DOMAIN, le reti amatoriali come la FidoNet, la Sublink, viene spesso devastato da quelle persone che hanno il coraggio di definirsi come "informatici" ma la cui visione di questa non va oltre al concetto di "fattura al cliente", costi quel che costi, anche a discapito di altre organizzazioni che basano la loro attività sul lavoro serio.
D'altra parte si sa che quando si tratta di software la spesa maggiore è quella legata alla progettazione, alla realizzazione e alla messa a punto.
Rubare un pacchetto di contabilità (per fare un esempio) elimina tutte e tre le fasi permettendo di passare subito alla quarta e cioè all'emissione della fattura al cliente. Lo stesso vale per molte ditte che si occupano di formazione professionale.
Queste spesso organizzano corsi di 30 ore con un numero minimo di 10 persone richiedendo importi che variano da 1.500.000 a più di 2.500.000 a persona.
Capite che un corso del genere permette di ricavare una media di 20.000.000 che tolto circa un 30% di tasse, 60.000 all'ora date al docente e 200.000 di spese varie si riducono a soli 12.000.000 di guadagno pulito per la società fatti i circa 6 giorni di lavoro.
Volete che questa spenda ancora 50.000 per iscritto al fine di fornirgli un volume di testo o che prendano uno e lo facciano stare un mese a scrivere e a comporre le dispense ? No !
È più comodo, veloce e si spende di meno a prendere tra gli scritti che circolano tra il pubblico dominio, cambiargli il copyright e fornirgli quelli. Voglio precisare che quello che ho detto non nasce da pure teorie astratte ma da esperienza personale fatta su casi realmente accaduti.
Alzi la mano chi di voi non è a conoscenza di qualche speculazione fatta mediante software rubato.
Il fascicolo è da considerasi come una semplice introduzione al linguaggio SQL per tutti quelli che vogliono avvicinarsi all' ottica della gestione dei database mediante questo.
Voglio precisare che la dispensa composta dalla parte 1 (questa) e dalla parte 2 (non ancora rilasciata tra il PD soft) è stata scritta per un corso legato all'utilizzo di SQL e di Informix SQL.
In ogni caso un ulteriore approfondimento può essere eseguito sui manuali specifici del prodotto SQL utilizzato anche non Informix.