Esercizio - Interrogazioni SQL sul database dei riferimenti bibliografici (slide di SQL)
In questa pagina 8
Testo (le 25 interrogazioni di esempio delle slide di SQL del corso di Basi di Dati, Ing. Informatica UniPD, sul database dei riferimenti bibliografici). Per ognuna: la richiesta, l'espressione di algebra relazionale (dove la slide la riporta), l'SQL e il risultato sui dati del corso (la base di dati bibliographicreferences, 9 autori, 4 editori, 9 riferimenti). Tutte le query sono state eseguite con SQLite sugli stessi dati.
Teoria: SQL - interrogazioni, join e aggregazioniUn'interrogazione SQL ha almeno SELECT e FROM; le altre clausole (WHERE, GROUP BY, HAVING, ORDER BY) sono opzionali e HAVING richiede GROUP BY. Ordine logico di valutazione: FROM, WHERE, GROUP BY, HAVING, SELECT, DISTINCT, ORDER BY. SELECT corrisponde a π (ma senza DISTINCT non elimina i duplicati), WHERE a σ, gli alias ad AS/ρ, i join a ⋈ (INNER, LEFT, RIGHT, FULL). WHERE seleziona le righe prima del raggruppamento, HAVING seleziona i gruppi dopo. Le funzioni COUNT, SUM, AVG, MIN, MAX ignorano i NULL (COUNT(*) conta le righe, DISTINCT toglie i duplicati dentro la funzione); con un aggregato o con GROUP BY nella SELECT possono comparire solo colonne di raggruppamento o aggregati. LIKE usa % (qualsiasi sequenza) e _ (un carattere). I confronti con NULL non sono veri: serve IS NULL. Un filtro sulla tabella di destra di un LEFT JOIN va nella clausola ON, non in WHERE, altrimenti il join diventa interno. Con interi COUNT(a)/COUNT(b) è una divisione intera: serve CAST.SQL - interrogazioni, join e aggregazioni →, 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 →; schema e DDL in 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 in 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 →.
Schema e dati
Author(id, name, surname), Publisher(id, name), Reference(id, title, year, publisher) con publisher Publisher, Information(reference, notes) con reference Reference, Write(reference, author, authorNumber) con chiavi esterne verso Reference e Author.
| Author: id | name | surname |
|---|---|---|
| BuckJR | John R. | Buck |
| CastelliD | Donatella | Castelli |
| CornellG | Gary | Cornell |
| GilardiG | Gianni | Gilardi |
| HorstmannCS | Cay S. | Horstmann |
| OppenheimAV | Alan V. | Oppenheim |
| PaganoP | Pasquale | Pagano |
| SchaferRW | Ronald W. | Schafer |
| TanenbaumAS | Andrew S. | Tanenbaum |
| Publisher: id | name |
|---|---|
| ECDL2002 | Proc. Sixth European Conference on Research and Advanced Technology for Digital Libraries (ECDL 2002) |
| McGrawHill | McGraw-Hill |
| PrenticeHall | Prentice Hall International Editions |
| Springer | Springer |
| Reference: id | title | year | publisher |
|---|---|---|---|
| CastelliPagano2002a | OpenDLib: a Digital Library Service System | 2002 | ECDL2002 |
| CastelliPagano2002b | Foundations of a Multidimensional Query Language for Digital Libraries | 2002 | ECDL2002 |
| ECDL2002 | Proc. 6th European Conference on Research and Advanced Technology for Digital Libraries (ECDL 2002) | 2002 | Springer |
| Gilardi1991 | Analisi Uno | 1991 | McGrawHill |
| HorstmannCornell2000 | Java 2 tecniche avanzate | 2000 | McGrawHill |
| HorstmannCornell2001 | Java 2 i fondamenti | 2001 | McGrawHill |
| OppenheimSchafer1999 | Discrete-Time Signal Processing | 1999 | PrenticeHall |
| Tanenbaum1996 | Computer Networks | 1996 | PrenticeHall |
| Tanenbaum2001 | Modern Operating Systems | 2001 | PrenticeHall |
Write (reference, author, authorNumber): CastelliPagano2002a: CastelliD 1, PaganoP 2; CastelliPagano2002b: CastelliD 1, PaganoP 2; Gilardi1991: GilardiG 1; HorstmannCornell2000: HorstmannCS 1, CornellG 2; HorstmannCornell2001: HorstmannCS 1, CornellG 2; OppenheimSchafer1999: OppenheimAV 1, SchaferRW 2, BuckJR 3; Tanenbaum1996 e Tanenbaum2001: TanenbaumAS 1. Information (reference, notes): CastelliPagano2002a "pp. 292 - 308", CastelliPagano2002b "pp. 251 - 265", Tanenbaum1996 "3rd edition", Tanenbaum2001 "2nd edition".
Proiezione, selezione, espressioni (1-7)
1. Nome e cognome di tutti gli autori. .
SELECT name, surname FROM Author;9 righe, una per autore (la chiave è id, quindi nessun duplicato anche senza DISTINCT).
2. Tutti gli editori. SELECT * FROM Publisher;: 4 righe (* = tutte le colonne).
3. Gli autori e il loro numero d'ordine. .
SELECT DISTINCT author, authorNumber FROM Write;9 coppie distinte. Il DISTINCT rende la query equivalente alla proiezione algebrica (in SQL senza DISTINCT i duplicati restano; qui non ce ne sono comunque).
4. Cognome e nome in una colonna.
SELECT surname || ' ' || name AS AuthorData FROM Author;Valori come Buck John R., Castelli Donatella, Tanenbaum Andrew S. (9 righe). || concatena, AS dà il nome alla colonna.
5. Il nome di Gilardi. .
SELECT name FROM Author WHERE surname = 'Gilardi';Risultato: Gianni.
6. Identificatore e titolo dei riferimenti del 1999 o del 2000, in ordine di titolo.
SELECT id, title FROM Reference WHERE year = 1999 OR year = 2000 ORDER BY title ASC;OppenheimSchafer1999 (Discrete-Time Signal Processing) e HorstmannCornell2000 (Java 2 tecniche avanzate), in ordine alfabetico di titolo.
7. Gli autori il cui cognome inizia per C.
SELECT name, surname FROM Author WHERE surname LIKE 'C%';Castelli e Cornell. % = sequenza qualsiasi; _ = un solo carattere.
Aggregazioni e raggruppamenti (8-15)
8. Il numero d'autore massimo.
SELECT MAX(authorNumber) AS MaxNumberAuthors FROM Write;Risultato: 3 (il terzo autore di Oppenheim-Schafer: Buck). La variante SELECT reference, MAX(authorNumber) FROM Write è errata: con un aggregato la SELECT non può contenere colonne non aggregate (PostgreSQL la rifiuta; SQLite la accetta e restituisce OppenheimSchafer1999, quasi per caso).
9. Quante righe ha Author. SELECT COUNT(*) AS AuthorRowsNumber FROM Author; 9.
10. Quanti autori diversi compaiono in Write.
SELECT COUNT(DISTINCT author) AS WriteAuthorsNumber FROM Write;9: tutti gli autori hanno scritto qualcosa (COUNT(author) darebbe 14, le righe di Write).
11. Gli autori che compaiono in Write (con GROUP BY senza aggregati).
SELECT author FROM Write GROUP BY author;9 righe: equivale a SELECT DISTINCT author FROM Write.
12. Per ogni autore il numero di riferimenti.
SELECT author, COUNT(*) FROM Write GROUP BY author;BuckJR 1, CastelliD 2, CornellG 2, GilardiG 1, HorstmannCS 2, OppenheimAV 1, PaganoP 2, SchaferRW 1, TanenbaumAS 2.
13. Gli autori con più di un riferimento (HAVING).
SELECT author, COUNT(*) FROM Write GROUP BY author HAVING COUNT(*) > 1;CastelliD, CornellG, HorstmannCS, PaganoP, TanenbaumAS (tutti con 2).
14. Come la 12, ma contando solo le righe con numero d'autore maggiore di 1 (WHERE).
SELECT author, COUNT(*) FROM Write WHERE authorNumber > 1 GROUP BY author;BuckJR 1, CornellG 2, PaganoP 2, SchaferRW 1. Il WHERE agisce prima del raggruppamento: gli autori che sono sempre primi (Castelli, Gilardi, ...) scompaiono.
15. Come la 14, tenendo solo i gruppi con più di una riga (WHERE e HAVING).
SELECT author, COUNT(*) FROM Write WHERE authorNumber > 1 GROUP BY author HAVING COUNT(*) > 1;CornellG 2, PaganoP 2. Prima WHERE (righe), poi GROUP BY, poi HAVING (gruppi).
Join (16-21)
16. Titolo dei riferimenti dell'editore McGraw-Hill. .
SELECT name, title
FROM Reference AS R INNER JOIN Publisher AS P ON R.publisher = P.id
WHERE name = 'McGraw-Hill';3 righe: Analisi Uno, Java 2 tecniche avanzate, Java 2 i fondamenti.
17. Quanti riferimenti per 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 3, ECDL 2, Springer 1.
18. Cognome degli autori e titolo dei loro riferimenti. (con la rinomina degli id).
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 riga di Write.
19. Titolo e note di ogni riferimento (anche senza note). .
SELECT title, notes
FROM Reference AS R LEFT JOIN Information AS I ON R.id = I.reference;9 righe: quattro con note (pp. 292-308, pp. 251-265, 3rd edition, 2nd edition) e cinque con NULL. Un join interno ne darebbe solo 4.
20. Quanti riferimenti non hanno informazioni aggiuntive.
SELECT COUNT(*)
FROM Reference AS R LEFT JOIN Information AS I ON R.id = I.reference
WHERE notes IS NULL;5: il left join aggiunge i riferimenti senza note con notes = NULL, WHERE ... IS NULL li isola. (Dopo il join esterno il filtro sul lato destro è proprio quello che si vuole: tiene i NULL.)
21. Per ogni editore e cognome d'autore, il numero di riferimenti, ordinato.
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;9 righe: per esempio McGraw-Hill: Cornell 2, Horstmann 2, Gilardi 1; Prentice Hall: Tanenbaum 2, Buck 1, Oppenheim 1, Schafer 1; ECDL: Castelli 2, Pagano 2. Osservazione: si raggruppa per cognome, non per A.id: se due autori diversi avessero lo stesso cognome sarebbero fusi in un gruppo.
Query nidificate (22-23)
22. Il massimo numero di riferimenti di un editore.
SELECT MAX(ReferencesNumber)
FROM (SELECT publisher, COUNT(id) AS ReferencesNumber
FROM Reference GROUP BY publisher) AS Counter;Risultato 3. Non esiste MAX(COUNT(...)): il conteggio per editore è una tabella derivata, e il massimo si calcola su quella.
23. L'editore (o gli editori) con il massimo numero di riferimenti. La slide procede per tentativi; gli errori intermedi sono istruttivi:
-- Tentativo 1: ERRATO, un aggregato non può stare in WHERE
SELECT publisher, ReferencesNumber
FROM (SELECT publisher, COUNT(id) AS ReferencesNumber FROM Reference GROUP BY publisher) AS Counter
WHERE ReferencesNumber = MAX(ReferencesNumber);
-- Tentativo 2: ERRATO, la sottoquery fa riferimento a "Counter", visibile solo nella query esterna
WHERE ReferencesNumber = (SELECT MAX(ReferencesNumber) FROM Counter);
-- Soluzione: si ripete la tabella derivata dentro la sottoquery
SELECT publisher, ReferencesNumber
FROM (SELECT publisher, COUNT(id) AS ReferencesNumber FROM Reference GROUP BY publisher) AS Counter
WHERE ReferencesNumber = (SELECT MAX(ReferencesNumber)
FROM (SELECT publisher, COUNT(id) AS ReferencesNumber
FROM Reference GROUP BY publisher) AS Counter2);Risultato: McGrawHill 3 e PrenticeHall 3 (a pari merito). La ripetizione si evita con una vista (si vede sotto) o con una CTE.
Con una vista (stesso risultato, provato):
CREATE VIEW PublisherReferences AS
SELECT publisher, COUNT(id) AS ReferencesNumber FROM Reference GROUP BY publisher;
SELECT publisher, ReferencesNumber FROM PublisherReferences
WHERE ReferencesNumber = (SELECT MAX(ReferencesNumber) FROM PublisherReferences);Divisione intera e insiemi (24-25)
24. Il numero medio di riferimenti per editore.
SELECT COUNT(id) / COUNT(DISTINCT publisher) FROM Reference; -- 9 / 4 = 2 (divisione intera)
SELECT CAST(COUNT(id) AS DOUBLE PRECISION) / COUNT(DISTINCT publisher) FROM Reference; -- 2,25Con due interi / è la divisione intera e dà 2. Si converte uno dei due operandi per ottenere 2,25 (in SQLite il tipo si chiama REAL).
25. I riferimenti senza informazioni aggiuntive (differenza). .
SELECT id FROM Reference
EXCEPT
SELECT reference FROM Information;5 righe: ECDL2002, Gilardi1991, HorstmannCornell2000, HorstmannCornell2001, OppenheimSchafer1999 (le stesse della query 20).
Esercizi aggiuntivi (stesso database)
A. Gli editori senza riferimenti. Dopo aver aggiunto un editore Wiley, le quattro forme NOT IN, NOT EXISTS, LEFT JOIN ... IS NULL, EXCEPT restituiscono Wiley. Se un riferimento ha publisher = NULL, la forma NOT IN (SELECT publisher FROM Reference) restituisce zero righe (verificato): si usa NOT EXISTS oppure si aggiunge WHERE publisher IS NOT NULL nella sottoquery.
B. I riferimenti più recenti della media del proprio editore (sottoquery correlata).
SELECT r.id, r.year, r.publisher FROM Reference r
WHERE r.year > (SELECT AVG(r2.year) FROM Reference r2 WHERE r2.publisher = r.publisher);4 righe: HorstmannCornell2000, HorstmannCornell2001, OppenheimSchafer1999, Tanenbaum2001.
C. I cognomi degli autori che non sono mai oltre il primo posto (NOT EXISTS).
SELECT surname FROM Author a
WHERE NOT EXISTS (SELECT * FROM Write w WHERE w.author = a.id AND w.authorNumber > 1);Castelli, Gilardi, Horstmann, Oppenheim, Tanenbaum.
D. Il trigger sull'ordine degli autori. In SQL avanzato - indici, transazioni e triggerUn file di record può essere heap (inserimento in ordine, ricerca per uguaglianza in media n/2 accessi), ordinato (log2 n), hash (costante, solo uguaglianza) o B-tree (log_F n, anche intervalli). Un indice è una struttura ausiliaria di coppie (chiave K, puntatore P) ordinate, molto più piccola della tabella: l'indice primario è sul campo di ordinamento del file ordinato (una chiave per blocco, non denso, log2(m)+1 accessi), l'indice secondario è su un campo qualsiasi (denso). CREATE [UNIQUE] INDEX, anche su espressioni; PostgreSQL usa tabelle heap con indici secondari B-tree (predefinito) o hash, e PRIMARY KEY e UNIQUE creano un indice implicito. Gli indici velocizzano le letture ma costano nelle scritture e in spazio. Una transazione è una sequenza atomica di operazioni con proprietà ACID (atomicità, consistenza, isolamento, durabilità): START TRANSACTION, COMMIT, SAVEPOINT, ROLLBACK [TO SAVEPOINT]. Un trigger è una regola evento-condizione-azione (ECA): BEFORE/AFTER, ROW/STATEMENT, WHEN, con funzione PL/pgSQL che usa NEW e OLD; serve per vincoli complessi e attributi derivati.SQL avanzato - indici, transazioni e trigger → c'è il trigger che impone che il primo autore di un riferimento abbia numero 1 e che non ci siano due numeri uguali; provato in SQLite su un riferimento nuovo.
Errori nel materiale del corso
- Nella slide sulla query 23, il tentativo con
FROM Counterdentro la sottoquery è presentato come passaggio: non è valido perchéCounterè un alias definito dallaFROMdella query esterna. - Alcune slide di esempio mostrano le tabelle con nomi italiani (
Riferimento,Autore) e le query con nomi inglesi (Reference,Author): è lo stesso schema in due lingue.
Versione ripasso
Schema: Author(id, name, surname), Publisher(id, name), Reference(id, title, year, publisher), Information(reference, notes), Write(reference, author, authorNumber). Teoria: SQL - interrogazioni, join e aggregazioniUn'interrogazione SQL ha almeno SELECT e FROM; le altre clausole (WHERE, GROUP BY, HAVING, ORDER BY) sono opzionali e HAVING richiede GROUP BY. Ordine logico di valutazione: FROM, WHERE, GROUP BY, HAVING, SELECT, DISTINCT, ORDER BY. SELECT corrisponde a π (ma senza DISTINCT non elimina i duplicati), WHERE a σ, gli alias ad AS/ρ, i join a ⋈ (INNER, LEFT, RIGHT, FULL). WHERE seleziona le righe prima del raggruppamento, HAVING seleziona i gruppi dopo. Le funzioni COUNT, SUM, AVG, MIN, MAX ignorano i NULL (COUNT(*) conta le righe, DISTINCT toglie i duplicati dentro la funzione); con un aggregato o con GROUP BY nella SELECT possono comparire solo colonne di raggruppamento o aggregati. LIKE usa % (qualsiasi sequenza) e _ (un carattere). I confronti con NULL non sono veri: serve IS NULL. Un filtro sulla tabella di destra di un LEFT JOIN va nella clausola ON, non in WHERE, altrimenti il join diventa interno. Con interi COUNT(a)/COUNT(b) è una divisione intera: serve CAST.SQL - interrogazioni, join e aggregazioni →, 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 →.
- Proiezione e selezione (1-7):
SELECT name, surname FROM Authordà 9 righe;SELECT DISTINCT author, authorNumber FROM Writeper la algebrica;surname || ' ' || name AS AuthorDataconcatena;WHERE surname = 'Gilardi'dà Gianni;year = 1999 OR year = 2000 ORDER BY titledà OppenheimSchafer1999 e HorstmannCornell2000;LIKE 'C%'dà Castelli e Cornell. - Aggregati (8-10):
MAX(authorNumber)= 3;COUNT(*)suAuthor= 9;COUNT(DISTINCT author)suWrite= 9. La varianteSELECT reference, MAX(authorNumber) FROM Writeè errata. - Raggruppamento (11-15):
GROUP BY authordà 9 gruppi, con BuckJR 1, CastelliD 2, CornellG 2, GilardiG 1, HorstmannCS 2, OppenheimAV 1, PaganoP 2, SchaferRW 1, TanenbaumAS 2.HAVING COUNT(*) > 1dà 5 autori.WHERE authorNumber > 1prima del gruppo dà BuckJR 1, CornellG 2, PaganoP 2, SchaferRW 1; con ancheHAVINGrestano CornellG e PaganoP.
SELECT author, COUNT(*) FROM Write WHERE authorNumber > 1 GROUP BY author HAVING COUNT(*) > 1;- Join (16-21): McGraw-Hill ha 3 titoli. Riferimenti per editore: McGraw-Hill 3, Prentice Hall 3, ECDL 2, Springer 1. Autori-titoli: 14 righe. Left join con
Information: 9 righe, di cui 5 connotesNULL;WHERE notes IS NULLdà 5. Raggruppare per cognome fonde gli omonimi.
SELECT name, title FROM Reference AS R INNER JOIN Publisher AS P ON R.publisher = P.id
WHERE name = 'McGraw-Hill';
SELECT COUNT(*) FROM Reference AS R LEFT JOIN Information AS I ON R.id = I.reference
WHERE notes IS NULL;- Massimo (22-23):
MAXsu tabella derivata = 3. ConWHERE ReferencesNumber = MAX(...)la query è errata: un aggregato non va inWHERE. ConFROM Counterdentro la sottoquery è errata: l'alias è visibile solo fuori. Soluzione: ripetere la tabella derivata, oppure vista o CTE. Risultato: McGrawHill e PrenticeHall, 3 ciascuno.
WITH Counter AS (SELECT publisher, COUNT(id) AS n FROM Reference GROUP BY publisher)
SELECT * FROM Counter WHERE n = (SELECT MAX(n) FROM Counter);- Cast e differenza (24-25): con interi, con
CAST(... AS DOUBLE PRECISION).EXCEPTdà 5 riferimenti senza informazioni: ECDL2002, Gilardi1991, HorstmannCornell2000, HorstmannCornell2001, OppenheimSchafer1999.
SELECT id FROM Reference EXCEPT SELECT reference FROM Information;- Extra: con un editore senza riferimenti
NOT INpuò dare zero righe se la sottoquery contiene NULL: si usaNOT EXISTS. Correlata: riferimenti più recenti della media del proprio editore, 4 righe.NOT EXISTSper gli autori mai oltre il primo posto: Castelli, Gilardi, Horstmann, Oppenheim, Tanenbaum.
SELECT surname FROM Author a
WHERE NOT EXISTS (SELECT * FROM Write w WHERE w.author = a.id AND w.authorNumber > 1);
SELECT r.id, r.year, r.publisher FROM Reference r
WHERE r.year > (SELECT AVG(r2.year) FROM Reference r2 WHERE r2.publisher = r.publisher);Errori tipici: aggregato in WHERE; colonne non aggregate con un aggregato; raggruppare per cognome; COUNT(*) con left join; divisione intera tra interi.