Manuale di SQL
(http://www.webmasterpoint.org/asp/sql2/home.asp)
1. Introduzione
Cosa Ŕ, a cosa serve, quali database supporta e altro...
2. Concetti generali
Le basi per l'utilizzo del linguaggio SQL
3. Select
SELECT e' il verbo di base che ci permette di eseguire interrogazioni sul sistema di database
4. Where
WHERE serve a definire il criterio di selezione dei record che devono essere rintracciati da SELECT
5. Operatori matematici di SQL 
Il linguaggio SQL dispone della possibilita' di implementazione dei normali operatori matematici per calcoli sui campi
6. Tavole 
Cosa sono e come funzionano le tavole in SQL
7. Subquery 
WHERE serve a definire il criterio di selezione dei record che devono essere rintracciati da SELECT
8. View
WHERE serve a definire il criterio di selezione dei record che devono essere rintracciati da SELECT
9. Modifica record
Fino a questo punto abbiamo visto soltanto la parte di linguaggio SQL adatto all'interrogazione
10. Creazione Tavole
Queste vengono create con il comando SQL, visto in un esempio iniziale e discusso successivamente, CREATE TABLE
11. Privilegi sulle Tavole
I permessi di accesso alle tavole
12. Fine
Ultime considerazioni 

 

SQL: Introduzione
SQL (pronunciato "esse-qu-elle") e' un linguaggio per la gestione di database molto semplice in quanto legato al metodo di espressione 
della lingua inglese.
Oltre a questo SQL e' 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 e' solo in parte esatta in quanto SQL puo' creare indici e file dati.
In ogni caso il metodo di gestione delle relazioni a livello fisico e' trasparente.
Al giorno d'oggi la maggior parte dei sistemi di database esistenti sono di tipo relazionale.
Esistono un infinita' 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.
Piu' precisamente l' SQL di cui parleremo e' quello definito dall' ANSI e quindi potrebbe risultare che alcuni prodotti posseggano un SQL piu' ricco.
Vediamo solo di accennare ad alcuni concetti legati alla teoria dei database.
Un approfondimento sulla teoria dei database puo' 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 

 

SQL: Concetti Generali
Un sistema relazionale e' un database che viene visto dall'utente come una collezione di tavole.
La tavola (table) e' l'unita' 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 piu' indici per sveltire l'accesso ai dati.
Come fara' SQL a utilizzarli possiamo dire che "sono fatti suoi".
In pratica il sistema informativo utilizzato ci permettera' di eseguire una suddivisione tra il livello fisico e quello logico.
Bisogna specificare che SQL e' 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 tavole.
Una tavola non e' altro che un insieme di righe e di colonne dove le prime sono i record e le seconde i vari campi che intendiamo 
trattare.
+-------------------------------------------+
| TAVOLA |
+-------+-------+---+-------+-------+-------+
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 tavola che rappresenta i clienti di una ditta XXXXX composta dal numero dell'utente (che deve essere unico), dal nome, dall'indirizzo e dalla citta'.

TAVOLA A
Num. # Nome Indirizzo Citta'
-------------------------------------
A001 Rossi V.Roma Torino
A002 Bianchi V.Trento Milano
A003 Verdi V.Trieste Genova
.... ....... ......... ......

Un' altra tavola 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.

TAVOLA B
Num. # Articolo Prezzo
-----------------------------
B001 Chiodo 150
B002 Bullone 300
B003 Dado 120
.... ....... ...

Una terza tavola potrebbe essere quella destinata alla memorizzazione delle vendite.
In pratica risulterÓ essere una connessione delle due tavole precedenti.

TAVOLA 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 tavole possono essere definite entita' mentre la terza relazione delle prime due.
Senza fare ancora nessun riferimento al linguaggio SQL potremmo definire le tavole nel seguente modo.

CREA TAVOLA A 
( NUM CHAR(5),
NOME CHAR(20),
INDIRIZZO CHAR(20),
CITTA CHAR(20) );

CREA TAVOLA B
( ANUM CHAR(5),
ARTICOLO CHAR(30),
PREZZO INTEGER );

CREA TAVOLA 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 tavole create come nell'esempio precedente ci mostra come di fatto e' possibile creare una descrizione logica del modello in modo indipendente dal suo livello fisico.
Potremmo suddividere i tipi di tavole in due classi.
La prima e' costituito dalle tavole di base (base table) che sono in pratica tavole reali esistenti fisicamente come files e come record salvati in questi.
Al contrario il secondo tipo e' costituito da tavole 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 tavole 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. 

 

SQL: Select

SELECT e' il verbo di base che ci permette di eseguire interrogazioni sul sistema di database (query).
Per utilizzare nella forma piu' generalizzata possibile SELECT e' sufficiente conoscere il solo nome della tavola da cui vogliamo estrarre informazioni.
Supponendo di avere una tavola 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 e' facile capire la metodologia per specificare i nomi delle colonne da visualizzare.
SELECT COL1, COL2, .... FROM ....
Il risultato dell'esecuzione di uno statement SELECT e' la creazione di una nuova tavola la quale puo' essere a vita breve, esistente solo sullo schermo del terminale da cui si richiede l'esecuzione, oppure definitiva.
La forma definitiva della tavola 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 puo' essere applicata a SELECT per definire i record che devono essere rintracciati.

 

SQL: Where

WHERE serve a definire il criterio di selezione dei record che devono essere rintracciati da SELECT.
Se avessimo una tavola XYZ del tipo:

NOME DATA IMPORTO
---------------------------
Rossi 12/03/89 120.000
Bianchi 13/03/89 35.000
Verdi 13/03/89 22.00
applicando
SELECT NOME FROM XYZ
WHERE IMPORTO < 100.000;
avremmo come risultato
NOME
-------
Bianchi
Verdi
In pratica diremmo di selezionare dalla tavola 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';
E' 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 e' possibile creare costrutti del tipo:
SELECT NOME, COGN FROM XYZ
WHERE COGN LIKE 'Rossi';
Utilizzare la LIKE come nell'esempio precedente e' come fare
SELECT NOME, COGN FROM XYZ
WHERE COGN = 'Rossi';
La vera potenza di LIKE sta' nella possibilita' di utilizzare due tipi di caratteri jolly per testare delle uguaglianze parziali.
I due caratteri sono precisamente '%' (percento) e '_' (underscore).
Il primo puo' valere uno o piu' caratteri mentre il secondo puo' sostituirne uno solo.
Supponendo di avere una tavola con:
DITTA CITTA' P.IVA
--------------------------------------
Bolognini Alessandria 01234567823
Bolognesi Firenze 02334432345
Bologna Milano 03121323423
Bognardi Roma 01212121212
potremmo dare
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 tavola 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.) e' IN.
Mediante IN e' possibile specificare una lista di valori da utilizzare come clausole di ricerca.
Un costrutto del tipo:
SELECT * FROM XYZ WHERE DITTA IN ('Ansaldo','Montedison');
selezionera' tutte le righe in cui la colonna DITTA risultera' essere nella lista (nell'esempio solo Ansaldo o Montedison).
Potremmo ad esempio disporre di una tavola 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 e' BETWEEN (compreso tra .... e ....).
Il comando
SELECT NOME FROM XYZ WHERE PREZZO BETWEEN 50000 AND 100000;
e' 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 e' 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 puo' 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 e'
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 tavola:
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 tavole ma influisce semplicemente sulla visualizzazione.
Vedremo l'utilizzo della specifica GROUP BY nel prossimo capitolo dopo aver introdotto gli operatori matematici.

 

SQL: Operatori matematici di SQL

Il linguaggio SQL, pur non essendo orientato al calcolo matematico, dispone della possibilita' di implementazione dei normali operatori matematici utilizzabili con lo statement SELECT per calcoli sui campi.
Disponendo di una tavola 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 e' 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 e' applicabile anche con SELECT COUNT().
Se volessimo, ad esempio, sapere quanti clienti hanno speso piu' 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 tavola dei clienti:
SELECT MIN(IMPORTO) FROM XYZ;
E' possibile usare come argomento degli operatori MIN e MAX anche dei calcoli matematici.
SELECT MAX(IMPORTO / 100) FROM XYZ;
L'operatore SUM e' 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 tavola (sempre quella riportata ad esempio nelle pagine precedenti).
Se dovessimo calcolare una media di tutti gli importi presenti nella tavola potremmo dare il comando:
SELECT (SUM(IMPORTO) / COUNT(IMPORTO)) FROM XYZ;
In pratica verrebbe calcolata la somma di tutti gli importi presenti nella tavola 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 e' AVG.
Usandolo l'esempio precedente diventerebbe :
SELECT AVG(IMPORTO) FROM XYZ;
Esiste un ulteriore specifica utilizzabile con l'operatore COUNT e' DISTINCT.
Una determinata colonna potrebbe contenere dei valori duplicati.
Se si desidera avere il conteggio non considerando i duplicati e' possibile creare costrutti del tipo:
SELECT COUNT(DISTINCT CLIENTE) FROM XYZ;
Supponiamo di avere una tavola 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 tavola ovvero 5.
Desiderando sapere il numero delle ditte a cui e' stato venduto del materiale dovremmo dare:
SELECT COUNT(DISTINCT CLIENTE) FROM XYZ;
Avremmo come risultato 3.
Altre tre funzioni di utilita' 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 tavola con:
CLIENTE IMPORTO
------------------
Rossi 100000
Verdi 120000
Bianchi 130000
e cercassimo di rintracciare qualche riga con
SELECT CLIENTE FROM XYZ WHERE CLIENTE = 'ROSSI';
non troverremmo 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 tavola 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 e' applicabile a GROUP BY.
Nel caso in cui si vuole eliminare alcuni gruppi e' possibile utilizzare la specifica HAVING.
E' possibile ad esempio creare costrutti del tipo:
SELECT CAMPO1 FROM XYZ GROUP BY CAMPO2 HAVING COUNT(*) > 1;

 

SQL: Tavole

Fino a questo punto abbiamo visto come ricercare dati da una determintata tavola.
In altre parole abbiamo considerato una tavola 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 tavole 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 tavola in cui queste erano contenute in quanto, di fatto, gli esempi erano sempre e solo relativi a una singola tavola.
Un determinato nome di colonna potrebbe essere comune a piu' tavole. 
Nel caso in cui SELECT deve ricercare dati da piu' tavole e' 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 piu' tavole.
SQL al fine di evitare questo tipo di problemi accetta la specifica del nome di colonna preceduto da nome della tavola in cui si trova questo.
Al fine di portare un esempio a quanto detto prima possiamo supporre di avere le seguenti due tavole.
TAVOLA AAA NOME INDIRIZZO
----------------
Rossi V.Roma
..... ......
TAVOLA 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 piu' tavole 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 e':
SELECT tavola1.campo, tavola2.campo ... FROM tavola1,tavola2;
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 e' utilizzabile anche in questo caso al fine delle righe dalla visualizzazione.
E' possibile creare costrutti del tipo:
SELECT AAA.*, BBB.* FROM AAA, BBB WHERE AAA.NOME = BBB.NOME;
oppure utilizzando anche piu' tavole
SELECT A.CITTA, B.NOME, C.IMPORTO FROM A, B, C;
SQL permette di assegnare un nuovo nome alle tavole di un determinato statement.
L'operazione e' normalmente conosciuta come ALIAS.
Una tavola ALIAS rimane attiva solo durante l'esecuzione dello statement.
Lo standard ANSI riconosce ALIAS con il nome di correlazione.
SELECT A.*,B.* FROM TAVOLA1 A, TAVOLA2 B WHERE A.CAMPO = B.CAMPO;
Assegna come ALIAS il nome A alla TAVOLA1 e B alla TAVOLA2.

 

SQL: Subquery

Per introdurre il concetto rivediamo l'utilizzo della specifica IN.
Avevamo detto che questo controlla se un valore e' presente in una lista.
In altre parole il predicato IN formulato con:
campo IN (a, b, c, ...);
e' equivalente a
campo = a OR campo = b OR campo = c OR ... ;
I valori della lista potrebbero essere quelli selezionati da uno statement SELECT e cioe' 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 tavola ZYX che possiedono come citta' 'Asti'.
Il primo SELECT visualizzerebbe i campi NOME e INDIRIZZO di tutte le righe il cui NOME e' presente nella lista creata dal costrutto precedente.
Sono anche possibili statement del tipo:
SELECT NOME, INDIRIZZO FROM TAVOLA1 WHERE CITTA IN
(SELECT CITTA FROM TAVOLA2 WHERE NUMERO IN
(SELECT NUMERO FROM TAVOLA3 WHERE IMPORTO > 50000));
oppure
SELECT NOME FROM TAVOLA1 WHERE CITTA = 'Asti' AND IMPORTO IN
(SELECT IMPORTO FROM TAVOLA2 WHERE IMPORTO > 50000);
Come avete sicuramente notato negli esempi precedenti e' stata utilizzata la specifica IN.
Se utente sa che una determinata sottointerrogazione ritorna esattamente un solo valore puo' utilizzare un normale operatore di confronto (=, >, <, ecc.).
Ad esempio:
SELECT NOME FROM TAVOLA1 WHERE CITTA =
(SELECT CITTA FROM TAVOLA2 WHERE NOME = 'Rossi');
oppure
SELECT NOME FROM TAVOLA1 WHERE IMPORTO <
(SELECT MAX(IMPORTO) FROM TAVOLA2);
Terminiamo questa panoramica sulle interrogazioni guardando ancora il concetto di unione.
Due interrogazioni con SELECT possono essere unite al fine di creare una tavola unica.
Il vocabolo che permette di eseguire questa funzione e' UNION la cui sintassi e':
SELECT .... FROM tavola1 UNION SELECT .... FROM tavola2;
Il seguente esempio crea una tavola che e' la somma delle due create dai comandi SELECT.
SELECT * FROM TAVOLA1 UNION SELECT * FROM TAVOLA2;
Nel caso che si vogliano eseguire piu' di due interrogazioni e' necessario utilizzare le parentesi rotonde per la suddivisione.
Un esempio:
SELECT NOME FROM TAVOLA1 UNION
(SELECT NOME FROM TAVOLA2 UNION SELECT INDIRIZZO FROM TAVOLA3);
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 gia' costruiti senza dovere, inizialmente, sforzarsi a immaginarsi come possono essere creati questi oggetti.
Proseguiremo ora con alcuni argomenti legati all'utilizzo dei prodotti gia' creati.

 

SQL: View

Inizialmente parlando di tavole avevamo fatto una distinzione.
In pratica avevamo detto che potevamo classificare queste in due classi differenti: le TAVOLE di BASE e le TAVOLE 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 tavole in realta' non esistono fisicamente ma soltanto virtualmente e quindi non occupano spazio nel database.
Questo tipo di tavole vengono definite in SQL come VIEWS (viste) e appaiono all'utente come se fossero reali.
E' possibile creare tavole di questo tipo con determinate condizioni, ad esempio per fare in modo che l'utente veda solo nella tavola tutti i record di quelli che abitano in una certa citta'.
Una VIEW puo' 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 tavola 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 TAVOLA1;
Lo statement crea una tavola virtuale, VIEW, chiamata PROVA 
contenente tutte le colonne (vedi '*' di SELECT) contenute nella TAVOLA1.
CREATE VIEW PROVA ( NM, IMP ) AS
SELECT NOME, IMPORTO FROM TAVOLA1;
Il precedente costrutto invece definisce la struttura della VIEW contenente i campi NM e IMP corrispondenti con quelli NOME e IMPORTO della tavola di base TAVOLA1.
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 TAVOLA1;
Un altro esempio potrebbe essere
CREATE VIEW PROVA (NM, TOTIMP) AS SELECT NOME, (IMPORTO + IVA) FROM TAVOLA;
Sono anche possibili costrutti del tipo:
CREATE VIEW PROVA AS SELECT NOME, IMPORTO FROM TAVOLA1 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 e' di fatto eseguito ma semplicemente salvato nel catalogo.
L'utente in ogni caso vede la VIEW creata come se fosse una tavola reale.
In questo caso disporra' di una tavola PROVA che puo' essere considerata come una finestra nella tavola reale dalla quale la VIEW deriva.
Le operazioni di update della view sono riflesse sulla tavola originale anche se di fatto bisogna tenere a mente che non sempre le VIEW sono updatabili.
Vedremo successivamente il perche' di questa affermazione dopo aver visto le operazioni di INSERT, UPDATE ecc.
L'utilita' di una VIEW e' legata ad alcuni aspetti quali ad esempio la possibilita' 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 puo' essere eseguita semplicemente utilizzando il comando:
DROP VIEW NOMEVIEW;

 

SQL: Modifica Record

Fino a questo punto abbiamo visto soltanto la parte di linguaggio SQL adatto all'interrogazione.
Lavorando su database spesso ci si ritrova nella necesitta' di apportare modifiche ai contenuti dei records.
SQL dispone del verbo UPDATE che ci permette di svolgere questo tipo di azioni.
La sintassi generalizzata e':
UPDATE TAVOLA SET .... ;
Un comando del tipo
UPDATE TAVOLA1 SET NOME = 'Rossi';
setterebbe tutti i record della TAVOLA1 inserendo nella colonna NOME il nome 'Rossi'.
Update puo' essere in questo caso veramente pericoloso dato che ha la possibilita' di cambiare tutti i record di una tavola.
La clausola WHERE puo' restringere il campo dei dei record su cui agire. Ad esempio
UPDATE TAVOLA1 SET NOME = 'Rossi' WHERE NOME = 'Bianchi';
rintraccierebbe le righe della tavola 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 tavola potrebbe essere:
TAVOLA 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 e' possibile cambiare records da piu' di una tavola 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 e' INSERT la cui sintassi generalizzata e':
INSERT INTO TAVOLA .... ;
I valori da inserire devono essere specificati tra parentesi dopo la parola VALUE.
Guardate il seguente esempio:
INSERT INTO TAVOLA 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 TAVOLA (NOME, IMPORTO) VALUES ('Rossi',100000);
Il valore NULL puo' essere utilizzato come valore.
In ogni caso l'assegnazione puo' anche non essere diretta ma frutto di un interrogazione eseguita mediante SELECT a patto che questa non sia eseguita sulla stessa tavola in cui si vuole inserire il valore.
C'e' 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 TAVOLA1 (NOME, IMPORTO) 
SELECT NOME, IMPORTO FROM TAVOLA2;
oppure anche con WHERE:
INSERT INTO TAVOLA1 (NOME, IMPORTO)
SELECT NOME, IMPORTO FROM TAVOLA2 WHERE CITTA = 'Roma';
La cancellazione di un record avviene mediante DELETE.
DELETE FROM TAVOLA;
cancella tutti i record della tavola.
Nel caso in cui si vogliono selezionare i records da cancellare bisogna utilizzare la parola WHERE.
DELETE FROM TAVOLA WHERE NOME = 'Rossi' AND CITTA = 'Roma';

 

SQL: Creazione Tavole

Dopo aver visto le metodologie per eseguire interrogazioni sulle tavole diamo un occhiata finalmente al modo di crearle.
Penso che a questo punto sia abbastanza chiaro che cosa si intende per tavola.
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 vogoliono avere nella tavola 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 e' quella di Informix SQL e che quindi in altri prodotti potrebbe essere piu' ridotta o piu' 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 data 
In alcuni casi si desidera che un determinato campo non possa assumere il valore NULL.
Se si desidera che SQL richieda in modo obbligatorio l'inserimento di un campo e' possibile specificare a seguito del tipo la parola NOT NULL.
Nel caso in cui avviene questa specifica SQL segnalera' un messaggio d'errore tutte le volte che si cerchera' di omettere l'inserimento.
Un esempio:
CREATE TABLE TAVOLA (
IDNUM CHAR(5) NOT NULL,
NOME CHAR(20),
INDIR CHAR(20),
IMPORTO INTEGER 
);
L' assegnazione al campo IDNUM e' obbligatorio.
Se si desidera che il valore di una determinata colonna sia unico in tutta la tabella e' possibile specificare la word UNIQUE.
Fate attenzione che UNIQUE puo' essere specificato solo con quei campi che sono anche stati definiti come NOT NULL.
Riprendendo l'esempio precedente: 
CREATE TABLE TAVOLA (
IDNUM CHAR(5) NOT NULL UNIQUE,
...... .........
); 
Nel caso in cui i campi da definire come UNIQUE siano piu' di uno e' possibile eseguire la specifica anche in fondo alla dichiarazione.
Prendiamo il seguente esempio:
CREATE TABLE TAVOLA (
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 TAVOLA (
IDNUM CHAR(5) NOT NULL,
DITTA CHAR(20) NOT NULL,
PIVA CHAR(11) NOT NULL,
UNIQUE (IDNUM,DITTA,PIVA)
);
E' possibile eliminare una tavola mediante
DROP TABLE NOMTAV;
Quando viene creata una tavola e successivamente si esegue un inserimento i dati vengono accodati a quelli gia' presenti senza 
che venga dato un preciso ordine.
Se i records sono molti la ricerca e l'accesso ai dati puo' 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 e' 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 e' 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 puo' essere completamente trasparente all'utente.
Molti prodotti come INFORMIX SQL richiedono, durante la procedura interattiva di creazione delle tavole, se i vari campi devono 
possedere un indice associato e se questo puo' contenere chiavi duplicate. La creazione da interprete di un indice puo' avvenire mediante:
CREATE INDEX NOMIND ON TAVOLA(CAMPO1, ....);
Se disponessimo di una tavola TAVOLA1 con i seguenti campi
NOME, INDIRIZZO, PIVA, IMPORTO
potremmo, ad esempio, creare un indice utilizzando come chiave NOME+PIVA.
CREATE INDEX NEWINDEX ON TAVOLA1(NOME, PIVA);
Se avessimo scelto di creare un indice in modo che in questo non possano risultare duplicazioni avremmo dovuto utilizzare la parola SQL UNIQUE nel seguente modo:
CREATE UNIQUE INDEX NEWINDEX ON TAVOLA1(NOME, PIVA);
L'eliminazione di un indice si ottiene mediante
DROP INDEX NOMIND;

 

SQL: Privilegi sulle Tavole

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 possibilita' che piu' persone possano lavorare vicino a questo, magari anche contemporaneamente.
Chiaramente, in questo caso, si deve regolamentare i permessi d'accesso alle tavole per fare in modo che ogni persona faccia esattamente quello che deve, o che puo', fare senza correre il pericolo che avvengano modifiche o operazioni non gradite sulle tavole da parte di persone estranee o non autorizzate.
Colui che crea un certa tavola puo' stabilire quali operazioni possono essere eseguite. 
SQL dispone della funzione GRANT destinata a questa operazione.
La sintassi generalizzata e':
GRANT lista[ALL] ON tavola TO lista[PUBLIC];
Ad esempio:
GRANT ALL ON TAVOLA1 TO PUBLIC;
permette a chiunque di eseguire qualsiasi statement sulla TAVOLA1.
GRANT INSERT, SELECT ON TAVOLA1 TO MARIO;
permette l'inserimento e il query all'utente identificato come Mario.
GRANT UPDATE(NOME,IMPORTO) ON TAVOLA1 TO MARIO;
permette a Mario di modificare i campi NOME e IMPORTO di TAVOLA1.
GRANT ALL ON TAVOLA1 TO MARIO,LUCY,FRANCO;
permette l'esecuzione di tutti gli statement a Mario, Lucy e a Franco.
La revoca dei permessi puo' essere eseguita con il verbo REVOKE.
Ad esempio:
REVOKE ALL ON TAVOLA1 FROM MARIO;
rimuove tutti i diritti a Mario.
REVOKE INSERT ON TAVOLA1 FROM MARIO;
rimuove il permesso d'inserimento a Mario.

 

SQL: Privilegi sulle Tavole

Con questo concludo questo breve fascicolo tralasciando tutta il trattamento dei report, dei form ecc.
Oltre tutto in questa parte e' stata omessa la parte legata alla programmazione con SQL.
Tutto questo e' presente nella seconda parte di questo testo che verra' utilizzato come dispensa per un corso che dovro' tenere tra circa due mesi e che quindi non e' ancora possibile lasciare tra il materiale PUBLIC DOMAIN.
In ogni caso, come gia' 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 e' 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 possibilita' di un linguaggio come questo.