Un
Db si dice relazionale quando al suo interno è possibile
stabilire relazioni tra sottoinsiemi di dati.
Facciamo un esempio: abbiamo 2 files, nel N.1 ci sono i dati
di tutti gli impiegati di una Società, nel N.2 ci sono
i dati di tutti i reparti della Società; se nel record
tipo 1 è presente il reparto di appartenenza allora
possiamo mettere in relazione gli impiegati con i rispettivi
reparti.
Naturalmente l'esistenza di relazioni pone delle restrizioni:
modifiche all'identificativo dei record tipo 2 o loro cancellazione
provoca la distruzione della relazione.
Un primo passo verso il Db relazionale nell'ambito Personal
Computer fu l'introduzione sia per il dBaseIII che per il
Silver-Fox del comando SET RELATION TO .
Vediamone l'uso e le limitazioni.
Si
usavano delle aree di lavoro, in ognuna delle quali era possibile
aprire un solo Db per volta; la sequenza dei comandi sarebbe
stata:
SELECT
1 sceglie l'area 1
USE REPARTI INDEX REPARTI apre REPARTI.DBF con indice
REPARTI.NDX (IDX per Silver-Fox)
SELECT 2
USE IMPIEGAT INDEX IMPIEGAT
SET RELATION TO REPARTO ON REPARTI |
col
vincolo che il campo REPARTO esistesse in entrambi e con lo
stesso formato ed inoltre fosse la Chiave dell'indice in uso
per REPARTI
Allora per ogni record di IMPIEGATI erano disponibili, oltre
ai campi di quel Db, anche i campi del Db REPARTI:
ad esempio A>NOMEREP, dove A> indicava l'Alias
del Db: A=area 1, B=area 2, etc..
Non
era molto ma si incominciava ad introdurre il concetto.
Anche perché nel frattempo erano nati i primi veri
Db relazionali, ma la complessità dei loro motori li
confinava su macchine più grandi e potenti: poveri
PC con i loro 640Kb di RAM!.
Uno di quei primi Db relazionali è ancora oggi vivo
e vegeto ed è Oracle, ma il requisito minimo per utilizzarlo
è un server Windows NT ed alcune decine di milioni
per la licenza.
Quindi
d'ora in avanti ci limiteremo a Database relazionali che siano
compatibili con i nostri PC, in particolare Microsoft Access
(ed il relativo motore: MS Jet) che, pur non essendo
l'unico, ha praticamente monopolizzato il mercato in ambiente
Windows. Pur essendo nato sotto Windows 3.1 la sua diffusione
su larga scala è iniziata con Windows 95 e la sua integrazione
nel pacchetto MS Office.
SQL
- Structured Query Language |
La
sua nascita è dovuta all'esigenza di stabilire una
interfaccia standard di accesso ai Database indipendentemente
dal tipo e dalla piattaforma.
Esamineremo in dettaglio l'SQL supportato dal motore MS Jet
(quello dei Data Access Object di Visual Basic) e dei comandi
SQL ignoreremo in genere quelli non utilizzabili in Visual
Basic.
»
SELECT Statement
Ordina
al motore Microsoft Jet di recuperare informazioni da un database
sotto forma di un insieme di records.
Sintassi:
SELECT
[predicate] { * | table.* | [table.]field1 [AS alias1]
[, [table.]field2 [AS alias2] [, ...]]}
FROM tableexpression [, ...] [IN externaldatabase]
[WHERE... ]
[GROUP BY... ] [HAVING... ]
[ORDER BY... ]
[WITH OWNERACCESS OPTION] |
Parti
del comando SELECT:
predicate:
uno dei seguenti: ALL, DISTINCT, DISTINCTROW, o TOP (vedremo
più avanti i dettagli).
Si usa il predicate per restringere il numero dei records
restituiti; se omesso il default è ALL.
(*):
Indica che sono scelti tutti i campi della Table (o delle
Tables).
table Il nome della Table contenente i campi dalla quale vengono
scelti i records.
field1, field2 Il nome dei campi contenenti i dati cercati.
Se si includono più campi vengono restituiti nell'ordine
elencato.
alias1, alias2 I nomi da usare come testata delle colonne
invece dei nomi di colonna originali della Table.
Da
notare che qualora i campi provengano da più Tables
in caso di sinonimi occorre utilizzare la sintassi nometable.nomefield
per determinare un campo in modo univoco.
»
clausola FROM
tableexpression: una espressione che identifica una
o più Tables contenenti i dati da cercare; l'espressione
può essere il nome di una Table, il nome di una Query
salvata o il risultato di una INNER JOIN, LEFT JOIN, o RIGHT
JOIN (vedremo più avanti i dettagli).
externaldatabase: il nome del Database contenente le
Tables in tableexpression se non è il Database corrente.
»
clausola WHERE
Indica il criterio di scelta: l'elemento base è nomefield
operatore di confronto valore|nomefield; possono essere concatenati
più elementi base tramite gli operatori logici (And
Or).
Esempio: WHERE NomeImpiegato="Paolo" And (ImportoOrdini>10000000
Or NumeroOrdini>100)
Note:
nell'eseguire questa operazione il motore database Microsoft
Jet cerca la Table (o Tables) specificata, estrae le colonne
(=campi) scelte, individua le righe (=record) che soddisfano
il criterio ed ordina o raggruppa le righe risultanti nell'ordine
specificato.
Il comando SELECT non modifica i dati del database.
La sintassi minima è:
Usando
un aterisco (*) si scelgono tutti i campi di una Table; l'esempio
seguente: SELECT * FROM Employees è equivalente ad
usare come RecordSet Employees.
»
Clausola GROUP BY
Combina
i records con valori identici del campo specificato in un
singolo record. Un valore riepilogativo è creato se
nel comando SELECT viene inclusa una funzione SQL di aggregazione,
quale Sum o Count.
Sintassi:
[GROUP BY groupfieldlist]
groupfieldlist Il nome di fino a 10 campi usati per raggruppare
i records; l'ordine dei campi in groupfieldlist determina
il livello di grouping da quello più alto a quello
più basso.
Note:
I valori riepilogativi vengono omessi se non è indicata
una funzione SQL di aggregazione nel comando SELECT.
Valori Null nei campi di GROUP BY vengono raggruppati e non
saltati; tuttavia i valori Null non vengono considerati da
alcuna funzione SQL di aggregazione.
Usare la clausola WHERE per escludere i records che non si
vuole raggruppare, usare la calusola HAVING per filtrare i
records dopo che sono stati raggruppati.
Non possono essere usati come campi GROUP BY né campi
Memo né campi OLE Object; possono invece essere usati
tutti gli altri campi di qualsiasi Table indicata nella clausola
FROM, anche se detti campi non sono inclusi nella lista della
SELECT purché sia inclusa almeno una funzione SQL di
aggregazione.
»
Clausola HAVING
Specifica quali records raggruppati vengono restituiti da
un comando SELECT con clausola GROUP BY.
Sintassi:
SELECT
.........
FROM ...........
WHERE ..........
GROUP BY groupfieldlist
HAVING groupcriteria |
groupfieldlist:
già esaminato in precedenza.
groupcriteria: un'espressione che determina quali records
raggruppati restituire; possono essere usate fino a 40 espressioni
collegate dagli operatori And e Or.
Esempio:
SELECT
CategoryID, Sum(UnitsInStock)
FROM Products
GROUP BY CategoryID
HAVING Sum(UnitsInStock) > 100 |
Dalla
Table Products per ogni CategoryID presente otteniamo un record
con 2 campi (CategoryID e UnitsInStock) contenente nel 2°
la somma dei valori di tutti i records avente uguale CategoryID
solo se detta somma è maggiore di 100.
»
Clausola ORDER BY
Ordina
i records risultato di una query secondo uno o più
campi specificati in sequenza ascendente o discendente.
Sintassi:
SELECT
.........
FROM ...........
[WHERE ..........]
[GROUP BY ...... [HAVING ....]]
[ORDER BY field1 [ASC|DESC][, field2 [ASC|DESC]][, ...]]] |
field1,
field2, I nomi dei campi di sort.
Note:
l'ordine di default è ascendente; i due esempi seguenti
sono equivalenti:
SELECT
LastName, FirstName
FROM Employees
ORDER BY LastName
SELECT
LastName, FirstName
FROM Employees
ORDER BY LastName ASC
|
Per
ordinare in senso discendente (Z ad A, 9 a 0) aggiungere DESC
dopo il nome del campo; l'esempio seguente ordina per stipendi
decrescenti:
SELECT
LastName, FirstName, Salary
FROM Employees
ORDER BY Salary DESC, LastName |
Non
è possibile specificare nella clausola ORDER BY né
campi Memo né campi OLE Object; i records vengono ordinati
sul 1° campo e quelli con ugual valore vengono quindi
ordinati sul 2° e così via.
Dichiarazione
WITH OWNERACCESS OPTION |
In
un ambiente multiutente con sicurezze a livello di workgroup,
utilizzare questa dichiarazione con una query per dare all'utente
della query le stesse abilitazioni del proprietario della
query.
L'esempio
seguente abilita l'utente a vedere dati salariali (anche se
non è abilitato ad accedere alla Table Stipendi) purché
il proprietario della query abbia tale abilitazione:
SELECT
Cognome, Nome, Salario
FROM Stipendi
ORDER BY Cognome
WITH OWNERACCESS OPTION |
Questa
opzione richiede che abbiate accesso al file System.mdw associato
al Database; è utile solo in implementazioni multiutente
protette.
» Predicati ALL, DISTINCT, DISTINCTROW, TOP
Specificano
i records restituiti dalla query SQL.
Sintassi.
SELECT
[ALL | DISTINCT | DISTINCTROW | [TOP n [PERCENT]]]
FROM table [ altre clausole .......] |
ALL
= tutti; assunto di default: il motore Microsoft Jet sceglie
tutti i records che soddisfano le condizioni nel comando SQL.
DISTINCT Omette i records contenenti dati duplicati
nei campi indicati; per esempio più impiegati nella
Table Employees possono avere lo stesso LastName.
Se 2 records contengono Smith nel campo LastName il seguente
comando SQL ritorna un solo record contenente Smith:
SELECT
DISTINCT LastName FROM Employees |
L'output
di una query che usa DISTINCT non è Updatable.
DISTINCTROW:
omette i dati basati su records interamente duplicati. Per
esempio voi create una query che unisce le Tables Customers
e Orders sulla base del campo CustomerID; Customers non contiene
CustomerID duplicati, ma Orders può in quanto ogni
customer può avere vari orders. Il seguente comando
SQL produce una lista delle compagnie che hanno almeno un
ordine (anche se senza alcun dettaglio riguardo agli ordini):
SELECT
DISTINCTROW CompanyName
FROM Customers INNER JOIN Orders
ON Customers.CustomerID = Orders.CustomerID
ORDER BY CompanyName |
DISTINCTROW
ha effetto solo si scelgono campi da alcune ma non tutte le
Tables usate nella query ed è ignorata se la query
include una sola Table o se si scelgono campi da tutte le
Tables.
TOP n [PERCENT]: ritorna solo un cero numero dei records
che cadono all'inizio o alla fine di una graduatoria specificata
da una clausola ORDER BY ; vogliamo i nomi dei migliori 25
studenti diplomati nel 1994:
SELECT
TOP 25 FirstName, LastName
FROM Students
WHERE GraduationYear = 1994
ORDER BY GradePointAverage DESC |
Se
si omette la clausola ORDER BY la query ritorna un insieme
arbitrario di 25 records della Table Students che soddisfano
la clausola WHERE.
TOP non sceglie tra valori uguali: se il 25° ed il 26°
studente hanno la stessa media la query restituisce 26 records.
Se si usa PERCENT viene restituita quella percentuale dei
records che soddisfano la query: se invece dei migliori 25
vogliamo il 10% peggiore dei diplomati 1994:
SELECT
TOP 10 PERCENT FirstName, LastName
FROM Students
WHERE GraduationYear = 1994
ORDER BY GradePointAverage ASC |
ASC
e DESC specificano il senso dell'ordinamento (ascending
o descending); il valore che segue TOP deve essere un intero
senza segno.
Operazioni
di JOIN, LEFT JOIN, RIGHT JOIN
in SQL |
»
INNER JOIN
Combina
records da due tables quando hanno valori corrispondenti in
un campo comune.
Sintassi:
FROM
table1 INNER JOIN table2 ON table1.field1 compareop table2.field2 |
table1,
table2: inomi delle Tables dalle quali combinare i records.
field1, field2: inomi dei campi oggetto della
join. Devono essere dello stesso data type e contenere la
stessa specie di dati, ma non è necessario che abbiano
lo stesso nome.
compareop: un' operatore relazionale: "=,"
"<," ">," "<=," ">=,"
o "<>."
Note:
se si usano come campi di join campi Memo o OLE Object si
causa un errore.
Tra due campi numerici è possibile la join solo se
sono dello stesso tipo: per esempio AutoNumber (Contatore)
e Long sono dello stesso tipo; Single e Double non sono dello
stesso tipo e si causa un errore.
La INNER JOIN combina i records da due Tables quando hanno
valori corrispondenti in un campo comune ad entrambe le Tables.
Potete usare INNER JOIN con le Tables Departments e Employees
per ottenere tutti gli impiegati di ogni dipartimento. Se
invece volete tutti i dipartimenti (anche se non hanno alcun
impiegato assegnato) o tutti gli impiegati (anche se qualcuno
non è assegnato ad alcun dipartimento), dovete usare
una operazione LEFT JOIN o RIGHT JOIN per creare una "outer
join".
L'esempio mostra la join delle Tables Categories and Products
tramite il campo CategoryID:
SELECT
CategoryName, ProductName
FROM Categories INNER JOIN Products
ON Categories.CategoryID = Products.CategoryID |
CategoryID
è il campo di join, ma non è incluso nell'output
della query non essendo incluso nella fields list della SELECT;
per includerlo aggiungere il nome del campo, ovvero Categories.CategoryID.
Si possono collegare più clausole ON in una JOIN usando
la seguente sintassi:
SELECT
fields
FROM table1 INNER JOIN table2
ON table1.field1 compopr table2.field1 AND
ON table1.field2 compopr table2.field2) OR
ON table1.field3 compopr table2.field3)] |
Si
possono nidificare le operazioni di JOIN usando la seguente
sintassi:
SELECT
fields
FROM table1 INNER JOIN
(table2 INNER JOIN [( ]table3
[INNER JOIN [( ]tablex [INNER JOIN ...)]
ON table3.field3 compopr tablex.fieldx)]
ON table2.field2 compopr table3.field3)
ON table1.field1 compopr table2.field2 |
Una
LEFT JOIN o una RIGHT JOIN possono essere nidificate dentro
una INNER JOIN, ma una INNER JOIN non può essere nidificata
entro una LEFT JOIN o una RIGHT JOIN.
»
LEFT JOIN, RIGHT JOIN
Sintassi:
FROM
table1 [ LEFT | RIGHT ] JOIN table2
ON table1.field1 compopr table2.field2 |
La
sintassi è identica a quella della INNER JOIN.
Note:
se si usano come campi di join campi Memo o OLE Object si
causa un errore.
L'operatore LEFT JOIN crea una join esterna sinistra, ovvero
include tutti i records della prima (left) Table anche se
non c'è corrispondenza con i records nella seconda
(right) Table.
L'operatore RIGHT JOIN crea una join esterna destra, ovvero
include tutti i records della seconda (right) Table anche
se non c'è corrispondenza con i records nella prima
(left) Table.
Per esempio useremo LEFT JOIN con le Tables Departments (left)
e Employees (right) per ottenere tutti i dipartimenti, inclusi
quelli che non hanno impiegati assegnati; per ottenere invece
tutti gli impiegati, inclusi quelli non assegnati ad un dipartimento,
useremo RIGHT JOIN.
LEFT
JOIN o RIGHT JOIN possono essere nidificate all'interno di
INNER JOIN, ma INNER JOIN non può essere nidificata
entro LEFT JOIN o RIGHT JOIN; per la sintassi di nidificazione
vale quanto detto per INNER JOIN, così come per le
clausole ON multiple ed i campi Memo o OLE Object.
»
SELECT .... INTO Statement
Query
di creazione di una Table.
Sintassi:
SELECT
field1[, field2[, ...]] INTO newtable [IN externaldatabase]
FROM source |
Crea
una nuova Table, eventualmente in un Db esterno, avente i
campi specificati nella fields list; non può essere
utilizzata per aggiungere records ad una Table esistente.
»
DELETE Statement
Query
di cancellazione dei records di una Table che soddisfano un
determinato criterio.
Sintassi:
DELETE
[table.*]
FROM table
WHERE criteria |
DELETE
serve per cancellazioni multiple; inoltre se la Table da cui
si cancella è posta in una relazione uno-a-molti con
altre Tables vengono cancellati anche i records relativi dalle
altre Tables.
Per esempio se la Table "Customers" è in
relazione uno-a-molti con la Table "Orders" cancellando
alcuni clienti da Customers si otterrà anche la cancellazione
degli ordini relativi da Orders.
»
INSERT INTO Statement
Query
di inserimento di uno o più records in una Table (append
query).
Sintassi:
(Multiple-record
append query):
INSERT
INTO target [(field1[, field2[, ...]])] [IN externaldatabase]
SELECT [source.]field1[, field2[, ...]
FROM tableexpression |
(Single-record
append query):
INSERT
INTO target [(field1[, field2[, ...]])]
VALUES (value1[, value2[, ...]) |
Qualora
la Table di destinazione abbia un campo Contatore (Autonumber)
non inserire tale campo nella fields list; la clausola FORM
può anche contenere JOIN di più Tables.
»
UPDATE, DELETE e INSERT Statements
Le applicazioni SQL-based effettuano gli aggiornamenti delle
Tables con l'esecuzione dei comandi UPDATE, DELETE ed INSERT.
Questi comandi fanno parte del "Minimum SQL grammar conformance
level" e devono essere supportati da tutti i drivers
e da tutti i Database.
Sintassi:
UPDATE
table-name
SET column-identifier = {expression | NULL}
[, column-identifier = {expression | NULL}]...
[WHERE search-condition]
DELETE
FROM table-name [WHERE search-condition]
INSERT
INTO table-name [(column-identifier [, column-identifier]...)]
{query-specification | VALUES (insert-value [, insert-value]...)}
|
Questi
comandi sono riportati a puro titolo esemplificativo non essendo
utilizzabili nella programmazione Visual Basic dove invece
si ricorre in genere a Metodi applicati agli Oggetti.
» "Interactive/On-line
SQL Tutorial" di F. Torres
» "SQL
Index"
»
"Interactive
SQL" di Jean Anderson
» "Database
glossary"
» "Starting
a Career in Databases"
» "A
pratical SQL Tutorial"
|