SQL - interrogazioni, join e aggregazioni
In questa pagina 10
Prerequisiti: SQL - definizione dei dati e vincoliSQL (standard ISO 9075) è un linguaggio dichiarativo con una parte di definizione dei dati (DDL: CREATE/DROP/ALTER di database, schemi, tipi enumerati, domini, tabelle) e una di manipolazione (DML: INSERT, DELETE, UPDATE, SELECT). Una tabella SQL è un multi-insieme di righe (può avere duplicati), quindi non è una relazione in senso stretto. CREATE TABLE elenca colonne con tipo e vincoli: NOT NULL, DEFAULT, CHECK (di colonna o di tupla), PRIMARY KEY (una sola, anche composta), UNIQUE, FOREIGN KEY ... REFERENCES con ON DELETE/ON UPDATE (NO ACTION, CASCADE, SET NULL, SET DEFAULT). I tipi principali sono CHAR/VARCHAR, NUMERIC, INTEGER, SERIAL, BOOLEAN, DATE, TIME, TIMESTAMP, più UUID, intervalli e JSON in PostgreSQL. DROP ... CASCADE cancella anche gli oggetti dipendenti, RESTRICT (predefinito) rifiuta se ce ne sono. DELETE e UPDATE senza WHERE agiscono su tutta la tabella.SQL - definizione dei dati e vincoli → (le tabelle usate negli esempi) e Algebra relazionale - operatori di base e joinLe operazioni sul modello relazionale sono di aggiornamento (inserimento, modifica, cancellazione, che possono violare i vincoli di integrità: si rifiuta, si corregge, si propaga con cascade o si imposta NULL/default) e di interrogazione (funzioni che da un'istanza del database producono una relazione). L'algebra relazionale è il linguaggio procedurale delle interrogazioni: gli operatori primitivi sono unione, differenza, prodotto, rinomina ρ, selezione σ e proiezione π; derivati sono intersezione, theta-join, equi-join, join naturale. Unione, intersezione e differenza richiedono relazioni compatibili all'unione (stessi attributi e domini). La selezione è una decomposizione orizzontale, la proiezione verticale (commutano solo se gli attributi della selezione sono nella proiezione). Il theta-join è σ_φ(R1×R2), il join naturale equivale a un equi-join sugli attributi omonimi seguito da una proiezione; la sua cardinalità è tra 0 e |R1||R2|, ≤|R1| se gli attributi comuni sono chiave di R2, =|R1| se sono anche chiave esterna di R1 con vincolo di integrità referenziale. Le tuple senza corrispondente sono "dangling". I valori NULL richiedono condizioni esplicite IS NULL / IS NOT NULL, perché σ_{A>40} ∪ σ_{A≤40} non restituisce le tuple con A nullo.Algebra relazionale - operatori di base e join → con Algebra relazionale estesa - divisione, aggregazione e outer joinL'algebra relazionale di base lavora una tupla alla volta e non sa fare calcoli sui valori né riassumere più tuple. L'estensione aggiunge: la proiezione generalizzata π_{F1,...,Fn}(R), che calcola espressioni sugli attributi (prezzo per quantità); l'aggregazione ℱ_{SUM, MAX, MIN, AVG, COUNT}(R), che produce una sola tupla; il raggruppamento (A1,...,Am)ℱ_{f(B)}(R), che produce una tupla per gruppo (grado n+m, cardinalità = numero di gruppi). Le funzioni di aggregazione ignorano i NULL, COUNT(*) conta le tuple, i NULL formano un gruppo a sé. Gli outer join (left, right, full) mantengono con NULL le tuple dangling del join; i NULL nella chiave di join non si accoppiano mai. La divisione R÷S dà gli x tali che (x,y) sta in R per ogni y di S, e si ottiene con π, × e −. Ricette: massimo = aggregazione + join con il massimo, "tutti" = divisione o complemento, "almeno due" = self-join.Algebra relazionale estesa - divisione, aggregazione e outer join →: SQL è la loro implementazione, con qualche differenza che si segnala. Si prosegue con SQL - query nidificate, operatori insiemistici e visteUna sottoquery è una SELECT tra parentesi dentro un'altra: nella FROM (tabella derivata, con alias obbligatorio in PostgreSQL) oppure nella WHERE, come valore scalare con =, <, ..., come insieme con IN / NOT IN, ANY, ALL, oppure con EXISTS / NOT EXISTS. Una sottoquery correlata usa colonne della query esterna e va rivalutata per ogni riga. Le funzioni di aggregazione non si possono usare in WHERE: il massimo si ottiene con una sottoquery scalare (WHERE n = (SELECT MAX(n) ...)), con una vista o una CTE. NOT IN con una sottoquery che contiene NULL non restituisce mai righe: meglio NOT EXISTS, EXCEPT o LEFT JOIN ... IS NULL. "Per tutti" (divisione) = doppio NOT EXISTS o HAVING COUNT(DISTINCT ...) = numero totale. Gli operatori insiemistici UNION, INTERSECT, EXCEPT richiedono query compatibili e di default eliminano i duplicati (ALL li mantiene). Una vista è una tabella virtuale definita da una query (CREATE VIEW, DROP VIEW): semplifica le query, riusa sottoquery, nasconde i dati; la vista materializzata invece memorizza il risultato.SQL - query nidificate, operatori insiemistici e viste →. Le interrogazioni degli esempi sono tutte nell'esercizio Esercizio - Interrogazioni SQL sul database dei riferimenti bibliografici (slide di SQL), con i risultati.
Gli esempi usano il database dei riferimenti bibliografici (tabelle Author(id, name, surname), Publisher(id, name), Reference(id, title, year, publisher), Information(reference, notes), Write(reference, author, authorNumber)), con 9 autori, 4 editori e 9 riferimenti.
Struttura di una query
SELECT <elenco di colonne o espressioni> -- obbligatoria
FROM <tabelle e join> -- obbligatoria
WHERE <condizione sulle righe>
GROUP BY <colonne di raggruppamento>
HAVING <condizione sui gruppi> -- solo con GROUP BY
ORDER BY <colonne> [ASC | DESC];L'ordine di scrittura non è l'ordine di valutazione. Logicamente il DBMS fa:
FROM(e i join): costruisce l'insieme di righe di partenza;WHERE: scarta le righe che non soddisfano la condizione;GROUP BY: forma i gruppi;HAVING: scarta i gruppi;SELECT: calcola le colonne di uscita (qui nascono gli alias);DISTINCT: toglie i duplicati;ORDER BY(eLIMIT): ordina il risultato.
Conseguenza: in WHERE non si usano gli alias definiti nella SELECT (in PostgreSQL; SQLite lo tollera) né le funzioni di aggregazione, che richiedono i gruppi; in ORDER BY invece sì.
SELECT: proiezione, espressioni e DISTINCT
SELECT name, surname FROM Author; -- π_{name,surname}(Author)
SELECT * FROM Publisher; -- tutte le colonneIl risultato elenca le 9 righe di Author (John R. Buck, Donatella Castelli, ...).
DISTINCT. Una proiezione algebrica elimina i duplicati, SELECT no. si scrive
SELECT DISTINCT author, authorNumber FROM Write;Senza DISTINCT si avrebbero le stesse righe qui (ogni coppia compare una volta, per via della chiave), ma in generale le righe duplicate restano. Regola pratica: per tradurre una in SQL si mette DISTINCT, a meno che la chiave non garantisca l'unicità.
Espressioni e alias. Nella SELECT (e in WHERE) si possono usare espressioni; AS dà un nome alla colonna di uscita:
SELECT surname || ' ' || name AS AuthorData FROM Author;|| concatena le stringhe; il risultato ha una colonna AuthorData con valori come Buck John R.. Con AS si dà un nome anche a una tabella (FROM Author AS A): serve per distinguere colonne omonime e per i self-join (è la rinomina ).
WHERE: condizioni
SELECT name FROM Author WHERE surname = 'Gilardi'; -- Gianni
SELECT id, title FROM Reference
WHERE year = 1999 OR year = 2000 ORDER BY title ASC; -- 2 righeOperatori: =, <> (o !=), <, >, <=, >=, AND, OR, NOT, e inoltre BETWEEN a AND b (estremi inclusi), IN (v1, v2, ...), LIKE, IS NULL. Con ORDER BY ... ASC | DESC si ordina (ASC predefinito); più colonne si separano con la virgola. LIMIT n tiene le prime righe (PostgreSQL e SQLite).
LIKE è una "povera" espressione regolare: % sostituisce una sequenza qualsiasi di caratteri (anche vuota), _ un solo carattere.
SELECT name, surname FROM Author WHERE surname LIKE 'C%'; -- Castelli, Cornell
SELECT name FROM Author WHERE name LIKE '_a%'; -- Gary, Cay S., PasqualeNULL e logica a tre valori
Un confronto con NULL non è vero né falso: è sconosciuto. Di conseguenza x = NULL e NULL = NULL non sono mai veri; per cercare i NULL si usa IS NULL / IS NOT NULL. WHERE tiene solo le righe per cui la condizione è vera. Con la logica a tre valori: NOT (1 = NULL) è sconosciuto, (1=1) OR (NULL=1) è vero, (1=2) AND (NULL=1) è falso (verificato). La tabella di verità è nella nota Algebra relazionale - operatori di base e joinLe operazioni sul modello relazionale sono di aggiornamento (inserimento, modifica, cancellazione, che possono violare i vincoli di integrità: si rifiuta, si corregge, si propaga con cascade o si imposta NULL/default) e di interrogazione (funzioni che da un'istanza del database producono una relazione). L'algebra relazionale è il linguaggio procedurale delle interrogazioni: gli operatori primitivi sono unione, differenza, prodotto, rinomina ρ, selezione σ e proiezione π; derivati sono intersezione, theta-join, equi-join, join naturale. Unione, intersezione e differenza richiedono relazioni compatibili all'unione (stessi attributi e domini). La selezione è una decomposizione orizzontale, la proiezione verticale (commutano solo se gli attributi della selezione sono nella proiezione). Il theta-join è σ_φ(R1×R2), il join naturale equivale a un equi-join sugli attributi omonimi seguito da una proiezione; la sua cardinalità è tra 0 e |R1||R2|, ≤|R1| se gli attributi comuni sono chiave di R2, =|R1| se sono anche chiave esterna di R1 con vincolo di integrità referenziale. Le tuple senza corrispondente sono "dangling". I valori NULL richiedono condizioni esplicite IS NULL / IS NOT NULL, perché σ_{A>40} ∪ σ_{A≤40} non restituisce le tuple con A nullo.Algebra relazionale - operatori di base e join →.
Funzioni di aggregazione
| Funzione | Significato |
|---|---|
COUNT(*) |
numero di righe (anche duplicate, anche con NULL) |
COUNT(c) |
numero di valori non NULL della colonna |
COUNT(DISTINCT c) |
numero di valori distinti non NULL |
SUM(c), AVG(c), MIN(c), MAX(c) |
somma, media, minimo, massimo dei valori non NULL |
Regole:
- i NULL sono scartati prima di applicare l'aggregato (
AVGdivide per il numero di valori non nulli); DISTINCTdentro la funzione ignora i duplicati;- quando la
SELECTcontiene una funzione di aggregazione senzaGROUP BY, può contenere solo aggregati:SELECT reference, MAX(authorNumber) FROM Writeè errato (PostgreSQL rifiuta la query; SQLite la accetta e restituisce una riga a caso, che è peggio).
SELECT MAX(authorNumber) AS MaxNumberAuthors FROM Write; -- 3
SELECT COUNT(*) AS AuthorRowsNumber FROM Author; -- 9
SELECT COUNT(DISTINCT author) AS WriteAuthorsNumber FROM Write; -- 9: ogni autore compare in almeno una rigaGROUP BY e HAVING
GROUP BY forma i gruppi di righe con gli stessi valori nelle colonne indicate; i NULL formano un gruppo a sé. Le funzioni di aggregazione si applicano a ciascun gruppo e producono una riga per gruppo. Nella SELECT possono comparire solo le colonne di raggruppamento e gli aggregati.
SELECT author, COUNT(*) FROM Write GROUP BY author;
-- una riga per autore: CastelliD 2, CornellG 2, ..., SchaferRW 1 (9 righe)HAVING filtra i gruppi, WHERE filtra le righe prima di raggruppare:
SELECT author, COUNT(*) FROM Write GROUP BY author HAVING COUNT(*) > 1;
-- 5 autori con almeno due riferimenti: CastelliD, CornellG, HorstmannCS, PaganoP, TanenbaumAS
SELECT author, COUNT(*) FROM Write WHERE authorNumber > 1 GROUP BY author;
-- WHERE agisce prima: si contano solo le righe con numero autore > 1 -> BuckJR 1, CornellG 2, PaganoP 2, SchaferRW 1
SELECT author, COUNT(*) FROM Write WHERE authorNumber > 1
GROUP BY author HAVING COUNT(*) > 1;
-- prima WHERE poi HAVING -> CornellG 2, PaganoP 2Regola: una condizione che riguarda una riga va in WHERE (è più efficiente: si scarta prima di raggruppare); una che riguarda un aggregato va in HAVING.
Join
Il join si scrive in FROM con la sintassi esplicita (preferibile alla vecchia con la virgola e le condizioni in WHERE):
| Tipo | Contiene |
|---|---|
INNER JOIN (o JOIN) |
solo le coppie che soddisfano ON |
LEFT [OUTER] JOIN |
il join interno più le righe di sinistra senza corrispondente, con NULL a destra |
RIGHT [OUTER] JOIN |
il join interno più le righe di destra senza corrispondente |
FULL [OUTER] JOIN |
entrambi |
SELECT name, title
FROM Reference AS R INNER JOIN Publisher AS P ON R.publisher = P.id
WHERE name = 'McGraw-Hill';
-- π_{name,title}(Reference ⋈_{publisher=id} σ_{name='McGraw-Hill'}(Publisher))
-- 3 righe: Analisi Uno, Java 2 tecniche avanzate, Java 2 i fondamentiUn join a più tabelle si scrive concatenando i JOIN; ogni ON collega l'ultima tabella aggiunta:
SELECT surname, title
FROM Author AS A INNER JOIN Write AS W ON A.id = W.author
INNER JOIN Reference AS R ON W.reference = R.id;
-- 14 righe: una per coppia (autore, riferimento)Join con raggruppamento. "Quanti riferimenti ha ogni editore":
SELECT name, COUNT(*)
FROM Reference AS R INNER JOIN Publisher AS P ON R.publisher = P.id
GROUP BY name;
-- McGraw-Hill 3, Prentice Hall International Editions 3, ECDL 2, Springer 1Il raggruppamento avviene dopo il join: COUNT(*) conta le righe del join, quindi attenzione se il join moltiplica le righe (un riferimento con tre autori compare tre volte).
Left join: tutte le righe di sinistra.
SELECT title, notes
FROM Reference AS R LEFT JOIN Information AS I ON R.id = I.reference;
-- 9 righe: i 4 riferimenti con note hanno le note, gli altri 5 hanno notes = NULL
SELECT COUNT(*)
FROM Reference AS R LEFT JOIN Information AS I ON R.id = I.reference
WHERE notes IS NULL;
-- 5: i riferimenti senza informazioniDove va la condizione:
ONoWHERE? Nei join esterni sono diverse. Con... LEFT JOIN Information I ON R.id = I.reference WHERE notes LIKE '%edition'si hanno solo 2 righe: ilWHEREscarta le righe connotes = NULL(NULLLIKEqualcosa non è vero) e il join esterno si comporta come uno interno. Con... ON R.id = I.reference AND notes LIKE '%edition'si hanno 9 righe: il filtro rimuove solo l'abbinamento (e lascia NULL a destra). Un filtro sulla tabella di destra di un left join va inON; un filtro sulla tabella di sinistra va inWHERE. Lo stesso vale per contare "zero" senza perdere righe:LEFT JOIN ... ON a.id = w.author AND w.authorNumber > 1 GROUP BY ... COUNT(w.reference)dà 0 per gli autori senza righe corrispondenti, conCOUNT(w.reference)(nonCOUNT(*), che darebbe 1 sulla riga con NULL).
Self-join. Per accoppiare righe della stessa tabella servono due alias: "ogni commento con la sua risposta", nel database del social network del laboratorio, è FROM Commento AS c INNER JOIN Commento AS r ON c.id = r.Commento_Origine.
Ordinare e unire più condizioni
SELECT P.name, A.surname, COUNT(R.id) AS ReferencesNumber
FROM Reference AS R INNER JOIN Publisher AS P ON R.publisher = P.id
INNER JOIN Write AS W ON R.id = W.reference
INNER JOIN Author AS A ON W.author = A.id
GROUP BY P.name, A.surname
ORDER BY P.name ASC, ReferencesNumber DESC, A.surname ASC;Per ogni coppia (editore, cognome) conta i riferimenti. Si ordina per editore, poi per numero decrescente, poi per cognome (si può ordinare per un alias della SELECT). Nota: si raggruppa per cognome, non per A.id: due autori diversi con lo stesso cognome verrebbero fusi. Per distinguerli si raggruppa anche per A.id.
Dall'algebra a SQL
Differenze da tenere presenti: SQL non elimina i duplicati da solo (DISTINCT, UNION senza ALL); i NULL; l'ordine delle righe è definito solo con ORDER BY.
Divisione intera e cast
Con interi, / è una divisione intera: SELECT COUNT(id) / COUNT(DISTINCT publisher) FROM Reference con 9 riferimenti e 4 editori dà 2, non 2,25. Per avere il decimale si converte un operando: CAST(COUNT(id) AS DOUBLE PRECISION) / COUNT(DISTINCT publisher) dà 2,25 (verificato; in SQLite il tipo si chiama REAL).
Errori tipici
- Colonne non aggregate e non di raggruppamento nella
SELECTconGROUP BYo con un aggregato (SELECT reference, MAX(authorNumber)). - Aggregati in
WHERE(WHERE n = MAX(n)): servonoHAVINGo una sottoquery (SQL - query nidificate, operatori insiemistici e visteUna sottoquery è una SELECT tra parentesi dentro un'altra: nella FROM (tabella derivata, con alias obbligatorio in PostgreSQL) oppure nella WHERE, come valore scalare con =, <, ..., come insieme con IN / NOT IN, ANY, ALL, oppure con EXISTS / NOT EXISTS. Una sottoquery correlata usa colonne della query esterna e va rivalutata per ogni riga. Le funzioni di aggregazione non si possono usare in WHERE: il massimo si ottiene con una sottoquery scalare (WHERE n = (SELECT MAX(n) ...)), con una vista o una CTE. NOT IN con una sottoquery che contiene NULL non restituisce mai righe: meglio NOT EXISTS, EXCEPT o LEFT JOIN ... IS NULL. "Per tutti" (divisione) = doppio NOT EXISTS o HAVING COUNT(DISTINCT ...) = numero totale. Gli operatori insiemistici UNION, INTERSECT, EXCEPT richiedono query compatibili e di default eliminano i duplicati (ALL li mantiene). Una vista è una tabella virtuale definita da una query (CREATE VIEW, DROP VIEW): semplifica le query, riusa sottoquery, nasconde i dati; la vista materializzata invece memorizza il risultato.SQL - query nidificate, operatori insiemistici e viste →). - Filtro sulla tabella di destra di un
LEFT JOINmesso inWHERE. COUNT(*)su un left join al posto diCOUNT(colonna_di_destra): i "senza corrispondenza" contano 1.- Dimenticare
DISTINCTdove la traduzione di una può dare duplicati. = NULLal posto diIS NULL.- Divisione intera tra
COUNT. - Raggruppare per una colonna non unica (cognome) quando serviva l'identificatore.
- Contare dopo un join che moltiplica le righe senza
COUNT(DISTINCT ...).
Domande d'esame
1. Differenza tra WHERE e HAVING.
Traccia. WHERE filtra le righe prima del raggruppamento e non può contenere aggregati; HAVING filtra i gruppi dopo il raggruppamento e di solito contiene aggregati. Si usa WHERE quando possibile (scarta prima, costa meno).
2. Come si conta il numero di commenti di ogni post, mostrando 0 per i post senza commenti?
Traccia. FROM Post p LEFT JOIN Commento c ON p.ID = c.Post GROUP BY p.ID con COUNT(c.ID). Con COUNT(*) i post senza commenti darebbero 1; con un INNER JOIN non comparirebbero.
3. Perché SELECT reference, MAX(authorNumber) FROM Write è errata?
Traccia. Con un aggregato e senza GROUP BY la SELECT può contenere solo aggregati: la colonna reference non ha un valore unico per il gruppo (l'intera tabella). Si ottiene la riga che ha il massimo con una sottoquery.
Versione ripasso
Struttura: SELECT ... FROM ... [WHERE] [GROUP BY] [HAVING] [ORDER BY]. Ordine di valutazione: FROM (e join), WHERE, GROUP BY, HAVING, SELECT, DISTINCT, ORDER BY. Per questo gli alias della SELECT non si usano in WHERE (PostgreSQL), ma sì in ORDER BY. Base: SQL - definizione dei dati e vincoliSQL (standard ISO 9075) è un linguaggio dichiarativo con una parte di definizione dei dati (DDL: CREATE/DROP/ALTER di database, schemi, tipi enumerati, domini, tabelle) e una di manipolazione (DML: INSERT, DELETE, UPDATE, SELECT). Una tabella SQL è un multi-insieme di righe (può avere duplicati), quindi non è una relazione in senso stretto. CREATE TABLE elenca colonne con tipo e vincoli: NOT NULL, DEFAULT, CHECK (di colonna o di tupla), PRIMARY KEY (una sola, anche composta), UNIQUE, FOREIGN KEY ... REFERENCES con ON DELETE/ON UPDATE (NO ACTION, CASCADE, SET NULL, SET DEFAULT). I tipi principali sono CHAR/VARCHAR, NUMERIC, INTEGER, SERIAL, BOOLEAN, DATE, TIME, TIMESTAMP, più UUID, intervalli e JSON in PostgreSQL. DROP ... CASCADE cancella anche gli oggetti dipendenti, RESTRICT (predefinito) rifiuta se ce ne sono. DELETE e UPDATE senza WHERE agiscono su tutta la tabella.SQL - definizione dei dati e vincoli →.
Algebra e SQL: = SELECT DISTINCT; = WHERE; = AS; = INNER JOIN ... ON; = aggregato nella SELECT; = GROUP BY; outer join = LEFT/RIGHT/FULL JOIN. SQL non toglie i duplicati da solo (Algebra relazionale estesa - divisione, aggregazione e outer joinL'algebra relazionale di base lavora una tupla alla volta e non sa fare calcoli sui valori né riassumere più tuple. L'estensione aggiunge: la proiezione generalizzata π_{F1,...,Fn}(R), che calcola espressioni sugli attributi (prezzo per quantità); l'aggregazione ℱ_{SUM, MAX, MIN, AVG, COUNT}(R), che produce una sola tupla; il raggruppamento (A1,...,Am)ℱ_{f(B)}(R), che produce una tupla per gruppo (grado n+m, cardinalità = numero di gruppi). Le funzioni di aggregazione ignorano i NULL, COUNT(*) conta le tuple, i NULL formano un gruppo a sé. Gli outer join (left, right, full) mantengono con NULL le tuple dangling del join; i NULL nella chiave di join non si accoppiano mai. La divisione R÷S dà gli x tali che (x,y) sta in R per ogni y di S, e si ottiene con π, × e −. Ricette: massimo = aggregazione + join con il massimo, "tutti" = divisione o complemento, "almeno due" = self-join.Algebra relazionale estesa - divisione, aggregazione e outer join →).
SELECT e WHERE:
SELECT surname || ' ' || name AS AuthorData FROM Author;
SELECT id, title FROM Reference WHERE year = 1999 OR year = 2000 ORDER BY title ASC;
SELECT name, surname FROM Author WHERE surname LIKE 'C%';DISTINCTper tradurre una :SELECT DISTINCT author, authorNumber FROM Write.- Operatori:
= <> < > <= >=,AND OR NOT,BETWEEN(estremi inclusi),IN,IS [NOT] NULL. LIKE:%sequenza qualsiasi,_un solo carattere.- Confronto con NULL: sconosciuto, mai vero.
WHEREtiene solo le righe vere.
Aggregati: COUNT(*) conta le righe; COUNT(c) i valori non NULL; COUNT(DISTINCT c) i valori distinti; SUM, AVG, MIN, MAX ignorano i NULL (AVG divide per i non nulli).
SELECT MAX(authorNumber) AS MaxNumberAuthors FROM Write; -- 3Con un aggregato e senza GROUP BY la SELECT può contenere solo aggregati: SELECT reference, MAX(authorNumber) FROM Write è errata.
GROUP BY e HAVING: i gruppi sono le righe con gli stessi valori nelle colonne indicate (i NULL formano un gruppo). La SELECT può contenere solo colonne di raggruppamento e aggregati.
SELECT author, COUNT(*) FROM Write GROUP BY author HAVING COUNT(*) > 1;
SELECT author, COUNT(*) FROM Write WHERE authorNumber > 1 GROUP BY author HAVING COUNT(*) > 1;- Il primo dà cinque autori con almeno due riferimenti (CastelliD, CornellG, HorstmannCS, PaganoP, TanenbaumAS).
- Il secondo filtra le righe con
WHEREprima del raggruppamento; con entrambe le clausole restano CornellG 2 e PaganoP 2. - Regola: una condizione su una riga va in
WHERE, una su un aggregato inHAVING.
Join:
SELECT name, title
FROM Reference AS R INNER JOIN Publisher AS P ON R.publisher = P.id
WHERE name = 'McGraw-Hill';INNER JOINdà solo le coppie che soddisfanoON;LEFTaggiunge le righe di sinistra senza corrispondenza, con NULL a destra;RIGHTil contrario;FULLentrambe.- Più join: ogni
ONcollega l'ultima tabella aggiunta.
Left join e conteggio degli zero: COUNT(*) su un left join conta 1 per il riferimento senza corrispondenza; COUNT(colonna_destra) dà 0.
SELECT COUNT(*) FROM Reference AS R LEFT JOIN Information AS I ON R.id = I.reference
WHERE notes IS NULL;ON o WHERE? Un filtro sulla tabella di destra di un left join va in
ON: inWHERE notes LIKE '%edition'il join si comporta come interno e restano 2 righe; inON ... AND notes LIKE '%edition'restano tutte le 9 righe, con NULL dove non c'è abbinamento. Un filtro sulla tabella di sinistra va inWHERE.
Self-join: due alias per la stessa tabella (FROM Commento AS c INNER JOIN Commento AS r ON c.id = r.Commento_Origine).
Cast: con interi / è divisione intera: dà 2. Per il decimale: CAST(x AS DOUBLE PRECISION).
Errori tipici
- Colonne non aggregate né di raggruppamento nella
SELECT. - Aggregati in
WHERE: servonoHAVINGo una sottoquery (SQL - query nidificate, operatori insiemistici e visteUna sottoquery è una SELECT tra parentesi dentro un'altra: nella FROM (tabella derivata, con alias obbligatorio in PostgreSQL) oppure nella WHERE, come valore scalare con =, <, ..., come insieme con IN / NOT IN, ANY, ALL, oppure con EXISTS / NOT EXISTS. Una sottoquery correlata usa colonne della query esterna e va rivalutata per ogni riga. Le funzioni di aggregazione non si possono usare in WHERE: il massimo si ottiene con una sottoquery scalare (WHERE n = (SELECT MAX(n) ...)), con una vista o una CTE. NOT IN con una sottoquery che contiene NULL non restituisce mai righe: meglio NOT EXISTS, EXCEPT o LEFT JOIN ... IS NULL. "Per tutti" (divisione) = doppio NOT EXISTS o HAVING COUNT(DISTINCT ...) = numero totale. Gli operatori insiemistici UNION, INTERSECT, EXCEPT richiedono query compatibili e di default eliminano i duplicati (ALL li mantiene). Una vista è una tabella virtuale definita da una query (CREATE VIEW, DROP VIEW): semplifica le query, riusa sottoquery, nasconde i dati; la vista materializzata invece memorizza il risultato.SQL - query nidificate, operatori insiemistici e viste →). - Filtro sulla tabella di destra di un
LEFT JOINmesso inWHERE. COUNT(*)su un left join al posto diCOUNT(colonna_destra).= NULLinvece diIS NULL; divisione intera traCOUNT.- Contare dopo un join che moltiplica le righe.
Esempi con risultati: Esercizio - Interrogazioni SQL sul database dei riferimenti bibliografici (slide di SQL). Seguito: SQL - query nidificate, operatori insiemistici e visteUna sottoquery è una SELECT tra parentesi dentro un'altra: nella FROM (tabella derivata, con alias obbligatorio in PostgreSQL) oppure nella WHERE, come valore scalare con =, <, ..., come insieme con IN / NOT IN, ANY, ALL, oppure con EXISTS / NOT EXISTS. Una sottoquery correlata usa colonne della query esterna e va rivalutata per ogni riga. Le funzioni di aggregazione non si possono usare in WHERE: il massimo si ottiene con una sottoquery scalare (WHERE n = (SELECT MAX(n) ...)), con una vista o una CTE. NOT IN con una sottoquery che contiene NULL non restituisce mai righe: meglio NOT EXISTS, EXCEPT o LEFT JOIN ... IS NULL. "Per tutti" (divisione) = doppio NOT EXISTS o HAVING COUNT(DISTINCT ...) = numero totale. Gli operatori insiemistici UNION, INTERSECT, EXCEPT richiedono query compatibili e di default eliminano i duplicati (ALL li mantiene). Una vista è una tabella virtuale definita da una query (CREATE VIEW, DROP VIEW): semplifica le query, riusa sottoquery, nasconde i dati; la vista materializzata invece memorizza il risultato.SQL - query nidificate, operatori insiemistici e viste →.
Esercizi su questo argomento
- Esercizio - Interrogazioni SQL sul database dei riferimenti bibliografici (slide di SQL)
- Esercizio - Interrogazioni sugli ordini di una pizzeria (tema d'esame 1 marzo 2013)
- Esercizio - Interrogazioni sulla compagnia di treni (tema d'esame 17 giugno 2013)
- Esercizio - Interrogazioni sulla mensa universitaria (tema d'esame 13 febbraio 2012)
- Esercizio - Interrogazioni sulla piattaforma di streaming musicale (tema d'esame 12 febbraio 2024)
- Esercizio - Interrogazioni sulle formule scientifiche (tema d'esame 22 gennaio 2025)
- Esercizio - Interrogazioni sullo stabilimento balneare (tema d'esame 18 giugno 2012)
- Esercizio - Laboratorio sul social network - DDL, DML e interrogazioni (laboratorio)
- Esercizio - Servlet delle prenotazioni con controllo delle sovrapposizioni (applicazione web)
- Esercizio - Tabella delle prenotazioni costruita con il DOM e filtrata (applicazione web)
- Esercizio - Triple RDF in Turtle e query SPARQL sulle aule (applicazione web)
Teoria collegata
- Algebra relazionale - operatori di base e join
- Algebra relazionale estesa - divisione, aggregazione e outer join
- Organizzazione fisica dei dati e accesso programmatico alle basi di dati
- SQL - definizione dei dati e vincoli
- SQL - query nidificate, operatori insiemistici e viste
- SQL avanzato - indici, transazioni e trigger
- Web semantico - RDF, linked open data e SPARQL