Salta al contenuto
Note per Studenti Esercizio - Interrogazioni sulle formule scientifiche (tema d'esame 22 gennaio 2025)

Esercizio - Interrogazioni sulle formule scientifiche (tema d'esame 22 gennaio 2025)

Esame
In questa pagina 7

Testo (esame di Basi di Dati, D.M. 270, 22 gennaio 2025, II e III parte, dal corso di Basi di Dati, Ing. Informatica UniPD). Degli studenti vogliono progettare un database per archiviare, categorizzare e collegare formule scientifiche e matematiche, una risorsa sulle loro origini, applicazioni ed evoluzione. Requisiti:

  • gli utenti del database (ricercatori, educatori, storici della scienza) non sono da memorizzare;
  • per ogni formula: il nome (per esempio legge di gravitazione di Newton), l'espressione (per esempio F=Gm1m2r2F=G\frac{m_1m_2}{r^2}), la descrizione, l'anno di scoperta (per esempio 1687), il campo di studio di riferimento e gli scienziati che l'hanno definita (ogni formula è definita da almeno uno scienziato);
  • per ogni scienziato: nome, cognome e campo di studio principale;
  • per ogni formula, le pubblicazioni scientifiche che la menzionano, di ciascuna con titolo, sommario, anno e un identificatore unico (per esempio un DOI); non si salvano pubblicazioni che non menzionano formule.

Interrogazioni, in SQL e in algebra relazionale (senza aggiungere attributi ridondanti):

  1. quali formule sono state attribuite a scienziati con cognome "Bernoulli";
  2. se esistono, gli scienziati che hanno la "Fisica" come campo di studio principale e che hanno definito formule per il campo di studio "Matematica";
  3. lo scienziato che ha definito il maggior numero di formule.

(Domanda 2, per la lode: modifiche allo schema ER e relazionale per rispondere in modo più efficiente ad almeno una query.)

La progettazione ER (formula come entità con ID seriale, campo di studio come entità e non come attributo, scienziato con id e non identificato da nome e cognome, pubblicazione con cardinalità (1,N)(1,N)) è nell'esercizio di progettazione della stessa traccia. 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 →, 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 →.

Progettazione ER e schema relazionale della stessa traccia: Esercizio - Progettazione ER e schema relazionale delle formule scientifiche (tema d'esame 22 gennaio 2025).

Schema relazionale (dalla soluzione del corso)

Chiavi in grassetto.

Relazione Attributi Note
PUBBLICAZIONE doi, titolo, sommario, anno
CAMPO_STUDIO id, nome il campo è un'entità (condivisa da formule e scienziati), identificata da un id seriale
FORMULA id, nome, descrizione, espressione, anno, campo →\to CAMPO_STUDIO l'id è un seriale: il nome non basta (due formule possono avere lo stesso nome e espressione diversa)
SCIENZIATO id, nome, cognome, campo →\to CAMPO_STUDIO nome e cognome non identificano (omonimi), né c'è un codice fiscale nel testo
R1R_1 pubblicazione →\to PUBBLICAZIONE, formula →\to FORMULA quali formule menziona una pubblicazione; vincolo: almeno una per pubblicazione
R3R_3 formula →\to FORMULA, scienziato →\to SCIENZIATO chi ha definito una formula; vincolo: almeno uno scienziato per formula

La relazione FORMULA–CAMPO_STUDIO è un'associazione uno-a-molti (una formula ha un campo): diventa la chiave esterna FORMULA.campo; lo stesso per SCIENZIATO.campo.

Dati inventati. CAMPO_STUDIO: 1 Fisica, 2 Matematica, 3 Chimica. SCIENZIATO (id, nome cognome, campo): 1 Jacob Bernoulli (Matematica), 2 Daniel Bernoulli (Fisica), 3 Isaac Newton (Fisica), 4 Carl Gauss (Matematica), 5 Marie Curie (Chimica). FORMULA (id, nome, campo): 1 gravitazione universale (Fisica), 2 equazione di Bernoulli dei fluidi (Fisica), 3 legge dei grandi numeri (Matematica), 4 legge di Gauss (Fisica), 5 minimi quadrati (Matematica), 6 equazione di Bernoulli per le EDO (Matematica), 7 binomio di Newton (Matematica), 8 decadimento radioattivo (Fisica), 9 seconda legge della dinamica (Fisica). R3R_3 (formula, scienziato): (1,3), (2,2), (3,1), (4,4), (5,4), (6,1), (6,2), (7,3), (8,5), (9,3). La formula 6 è stata definita da due Bernoulli.


Query 1: le formule attribuite a scienziati con cognome "Bernoulli"

Idea. Si parte da SCIENZIATO filtrato per cognome, si passa per R3R_3 (chi ha definito cosa) e si arriva a FORMULA: una catena di due join.

Algebra. La rinomina degli attributi omonimi (id, nome) di SCIENZIATO evita ambiguità con FORMULA:

SC←ρids,nomes,campos←id,nome,campo(SCIENZIATO)\text{SC}\leftarrow\rho_{\text{ids},\text{nomes},\text{campos}\leftarrow\text{id},\text{nome},\text{campo}}(\text{SCIENZIATO}) πid,nome,descrizione,espressione,anno(FORMULA⋈id=formulaR3⋈scienziato=idsσcognome=′Bernoulli′(SC))\pi_{\text{id},\text{nome},\text{descrizione},\text{espressione},\text{anno}}\Big(\text{FORMULA}\bowtie_{\text{id}=\text{formula}}R_3\bowtie_{\text{scienziato}=\text{ids}}\sigma_{\text{cognome}='\text{Bernoulli}'}(\text{SC})\Big)

(la soluzione del corso proietta id,descrizione,espressione,anno\text{id},\text{descrizione},\text{espressione},\text{anno} e dimentica nome; il nome della formula è utile e l'SQL del corso lo seleziona: conviene tenerlo in entrambi.) La proiezione elimina i duplicati.

SQL.

sql
SELECT DISTINCT f.id, f.nome, f.descrizione, f.espressione, f.anno
FROM formula AS f
     INNER JOIN r3 AS r ON f.id = r.formula
     INNER JOIN scienziato AS s ON r.scienziato = s.id
WHERE s.cognome = 'Bernoulli';

Risultato: formule 2 (equazione di Bernoulli dei fluidi, di Daniel), 3 (legge dei grandi numeri, di Jacob) e 6 (equazione di Bernoulli per le EDO, di Jacob e Daniel).

Errore nella soluzione del corso. L'SQL del corso manca del DISTINCT. La formula 6 è definita da due Bernoulli, quindi il join produce due righe per essa: senza DISTINCT la query restituisce 4 righe (2, 3, 6, 6) mentre l'algebra, dove la proiezione elimina i duplicati, ne dà 3. Algebra e SQL del corso non sono equivalenti. L'esempio è realistico: quando una proiezione scarta la chiave del join (qui scienziato), i duplicati sono da attendersi.

Query 2: scienziati di Fisica che hanno definito formule di Matematica

Idea. Due condizioni sul "campo", che sono due cose diverse: il campo principale dello scienziato (SCIENZIATO.campo) e il campo della formula (FORMULA.campo). Entrambi sono identificatori di CAMPO_STUDIO, non nomi: per confrontarli con 'Fisica' e 'Matematica' bisogna passare per CAMPO_STUDIO due volte (due ruoli diversi, quindi due alias).

Soluzione del corso (algebra).

FISICA←σcampo=′Fisica′(SCIENZIATO)FORM_MAT←σcampo=′Matematica′(FORMULA)\text{FISICA}\leftarrow\sigma_{\text{campo}='\text{Fisica}'}(\text{SCIENZIATO})\qquad\text{FORM\_MAT}\leftarrow\sigma_{\text{campo}='\text{Matematica}'}(\text{FORMULA}) FORM_MAT←ρIDF,nomeF←id,nome(πid,nome(FORM_MAT))πid,nome,cognome(FORM_MAT⋈IDF=formulaR3⋈scienziato=idFISICA)\text{FORM\_MAT}\leftarrow\rho_{\text{IDF},\text{nomeF}\leftarrow\text{id},\text{nome}}\big(\pi_{\text{id},\text{nome}}(\text{FORM\_MAT})\big)\qquad\pi_{\text{id},\text{nome},\text{cognome}}\big(\text{FORM\_MAT}\bowtie_{\text{IDF}=\text{formula}}R_3\bowtie_{\text{scienziato}=\text{id}}\text{FISICA}\big)

sql
SELECT s.id, s.nome, s.cognome
FROM scienziato AS s
     INNER JOIN r3 AS r ON s.id = r.scienziato
     INNER JOIN formula AS f ON r.formula = f.id
WHERE f.campo = 'Matematica' AND s.campo = 'Fisica';

Errori nella soluzione del corso. (1) campo è un identificatore (chiave esterna verso CAMPO_STUDIO.id), non il nome del campo: f.campo = 'Matematica' confronta un intero con una stringa e non trova nulla (provato: 0 righe). Bisogna fare il join con CAMPO_STUDIO e filtrare su nome. (2) Come nella query 1, manca il DISTINCT: uno scienziato che ha definito due formule di Matematica comparirebbe due volte. (3) La rinomina ρIDF,nomeF←…\rho_{\text{IDF},\text{nomeF}\leftarrow\dots} è corretta, ma serve anche a evitare il conflitto tra gli id di FORMULA e di SCIENZIATO.

Soluzione corretta (SQL).

sql
SELECT DISTINCT s.id, s.nome, s.cognome
FROM scienziato AS s
     INNER JOIN campo_studio AS cs ON cs.id = s.campo           -- campo principale dello scienziato
     INNER JOIN r3 AS r ON r.scienziato = s.id
     INNER JOIN formula AS f ON f.id = r.formula
     INNER JOIN campo_studio AS cf ON cf.id = f.campo           -- campo della formula
WHERE cs.nome = 'Fisica' AND cf.nome = 'Matematica';

campo_studio compare due volte, con alias cs e cf perché ha due ruoli. Risultato: Daniel Bernoulli (Fisica; ha definito la formula 6, di Matematica) e Isaac Newton (Fisica; ha definito il binomio di Newton, di Matematica). Jacob Bernoulli e Gauss hanno la Matematica come campo principale: esclusi; Curie ha la Chimica.

Algebra corretta. Si seleziona sul nome del campo e si porta l'id con un join, con le rinomine per gli id:

FIS←πid,nome,cognome(SCIENZIATO⋈campo=cidρcid,cnome←id,nome(σnome=′Fisica′(CAMPO_STUDIO)))\text{FIS}\leftarrow\pi_{\text{id},\text{nome},\text{cognome}}\Big(\text{SCIENZIATO}\bowtie_{\text{campo}=\text{cid}}\rho_{\text{cid},\text{cnome}\leftarrow\text{id},\text{nome}}\big(\sigma_{\text{nome}='\text{Fisica}'}(\text{CAMPO\_STUDIO})\big)\Big) FM←ρfid←id(πid(FORMULA⋈campo=midρmid,mnome←id,nome(σnome=′Matematica′(CAMPO_STUDIO))))\text{FM}\leftarrow\rho_{\text{fid}\leftarrow\text{id}}\Big(\pi_{\text{id}}\big(\text{FORMULA}\bowtie_{\text{campo}=\text{mid}}\rho_{\text{mid},\text{mnome}\leftarrow\text{id},\text{nome}}(\sigma_{\text{nome}='\text{Matematica}'}(\text{CAMPO\_STUDIO}))\big)\Big) πsid,snome,scogn(ρsid,snome,scogn←id,nome,cognome(FIS)⋈sid=scienziato(FM⋈fid=formulaR3))\pi_{\text{sid},\text{snome},\text{scogn}}\Big(\rho_{\text{sid},\text{snome},\text{scogn}\leftarrow\text{id},\text{nome},\text{cognome}}(\text{FIS})\bowtie_{\text{sid}=\text{scienziato}}\big(\text{FM}\bowtie_{\text{fid}=\text{formula}}R_3\big)\Big)

Provato con un interprete di algebra: stesso risultato dell'SQL (Daniel Bernoulli e Newton).

Query 3: lo scienziato che ha definito il maggior numero di formule

Idea. Il pattern del massimo: (1) numero di formule per scienziato, (2) il massimo di questi numeri, (3) gli scienziati con quel valore.

Algebra.

CONTEGGIO_F←ρscienziato,conteggio(scienziatoFCOUNT(∗)(R3))MAX_C←ρmaxc←MAX(conteggio)(FMAX(conteggio)(CONTEGGIO_F))\text{CONTEGGIO\_F}\leftarrow\rho_{\text{scienziato},\text{conteggio}}\big({}_{\text{scienziato}}\mathcal{F}_{\text{COUNT}(*)}(R_3)\big)\qquad\text{MAX\_C}\leftarrow\rho_{\text{maxc}\leftarrow\text{MAX}(\text{conteggio})}\big(\mathcal{F}_{\text{MAX}(\text{conteggio})}(\text{CONTEGGIO\_F})\big) πscienziato,conteggio(CONTEGGIO_F⋈conteggio=maxcMAX_C)\pi_{\text{scienziato},\text{conteggio}}\big(\text{CONTEGGIO\_F}\bowtie_{\text{conteggio}=\text{maxc}}\text{MAX\_C}\big)

La soluzione del corso scrive l'ultimo passo come σconteggio=MAX_C(CONTEGGIO_F)\sigma_{\text{conteggio}=\text{MAX\_C}}(\text{CONTEGGIO\_F}), con MAX_C una relazione: è una forma abbreviata del join.

SQL del corso (con vista):

sql
CREATE VIEW Conteggio_F AS
SELECT scienziato, COUNT(*) AS conteggio
FROM r3
GROUP BY scienziato;

SELECT scienziato, conteggio
FROM Conteggio_F
WHERE conteggio = (SELECT MAX(conteggio) FROM Conteggio_F);

Risultato sui dati: scienziato 3 (Newton), con 3 formule (1, 7, 9); gli altri ne hanno 2 (Daniel, Jacob, Gauss) o 1 (Curie). La vista evita di ripetere la tabella derivata. La query restituisce tutti gli scienziati a pari merito: se Gauss avesse 3 formule, comparirebbero entrambi (se ne avesse 4 vincerebbe lui, provato).

Per avere nome e cognome basta unire con SCIENZIATO:

sql
SELECT s.id, s.nome, s.cognome, COUNT(*) AS formule
FROM scienziato AS s INNER JOIN r3 ON r3.scienziato = s.id
GROUP BY s.id, s.nome, s.cognome
HAVING COUNT(*) = (SELECT MAX(n) FROM (SELECT COUNT(*) AS n FROM r3 GROUP BY scienziato) AS t);

(Si raggruppa per s.id: si evitano omonimi fusi in un gruppo.) Uno scienziato senza formule non compare in R3R_3, ma per il massimo non conta. Nell'ER del corso ogni formula ha almeno uno scienziato; il viceversa non vale.

Domanda 2 (per la lode): modifiche per rispondere più in fretta

  • Query 3: serve contare le righe di R3R_3 per scienziato e prendere il massimo. Un attributo derivato n_formule in SCIENZIATO (aggiornato con un trigger a ogni inserimento o cancellazione in R3R_3) rende la query una selezione: più veloce in lettura, ma ridondanza da mantenere coerente. Alternativa senza ridondanza: indice su R3(scienziato)R_3(\text{scienziato}) e una vista materializzata dei conteggi.
  • Query 2: il campo di studio è un'entità, quindi servono due join con CAMPO_STUDIO solo per confrontare i nomi. Se il nome del campo fosse l'identificatore (chiave naturale), SCIENZIATO.campo e FORMULA.campo conterrebbero già 'Fisica' e 'Matematica' e i due join sparirebbero; il prezzo è una chiave testuale ripetuta (spazio) e la difficoltà di rinominare un campo (aggiornamenti a cascata).
  • Query 1: un indice su SCIENZIATO(cognome) e su R3(scienziato)R_3(\text{scienziato}) rende veloce la ricerca per cognome; nessuna modifica di schema.

In ogni caso va valutato il carico: quanto spesso si fa la query rispetto a quanto spesso si modificano i dati (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 →).

Errori nel materiale del corso (riassunto)

  • Query 1: SQL senza DISTINCT (formula con due Bernoulli compare due volte); nome della formula assente dall'algebra ma presente nell'SQL.
  • Query 2: confronto di campo (un identificatore) con una stringa: manca il join con CAMPO_STUDIO; manca DISTINCT.
  • Query 3: σ\sigma con una relazione al posto del join (abbreviazione); il resto è corretto.

Versione ripasso

Schema: PUBBLICAZIONE(doi, titolo, sommario, anno), CAMPO_STUDIO(id, nome), FORMULA(id, nome, descrizione, espressione, anno, campo), SCIENZIATO(id, nome, cognome, campo), R1R_1(pubblicazione, formula), R3R_3(formula, scienziato). 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 →.

  1. Formule attribuite a un Bernoulli. Catena di due join: FORMULA ⋈\bowtie R3R_3 ⋈\bowtie SCIENZIATO filtrato per cognome = 'Bernoulli'. Risultato: formule 2, 3 e 6. La formula 6 ha due Bernoulli, quindi DISTINCT è indispensabile: senza, le righe sono 4; in algebra la π\pi elimina il duplicato.
sql
SELECT DISTINCT f.id, f.nome, f.descrizione, f.espressione, f.anno
FROM formula AS f INNER JOIN r3 AS r ON f.id = r.formula
     INNER JOIN scienziato AS s ON r.scienziato = s.id
WHERE s.cognome = 'Bernoulli';
  1. Scienziati di Fisica che hanno definito formule di Matematica. In algebra: si selezionano i campi per nome con σnome=′Fisica′\sigma_{\text{nome}='\text{Fisica}'} e σnome=′Matematica′\sigma_{\text{nome}='\text{Matematica}'}, si porta l'id del campo con un join e si rinomina per evitare gli omonimi; poi si collega a R3R_3 e si proietta su id, nome e cognome. campo è un identificatore di CAMPO_STUDIO, non un nome: f.campo = 'Matematica' confronta un intero con una stringa e dà zero righe. Serve un join con CAMPO_STUDIO due volte (alias cs per lo scienziato, cf per la formula), con filtro su nome. Risultato: Daniel Bernoulli e Isaac Newton. Jacob Bernoulli e Gauss hanno la Matematica come campo principale, quindi sono esclusi.
sql
SELECT DISTINCT s.id, s.nome, s.cognome
FROM scienziato AS s
     INNER JOIN campo_studio AS cs ON cs.id = s.campo
     INNER JOIN r3 AS r ON r.scienziato = s.id
     INNER JOIN formula AS f ON f.id = r.formula
     INNER JOIN campo_studio AS cf ON cf.id = f.campo
WHERE cs.nome = 'Fisica' AND cf.nome = 'Matematica';
  1. Scienziato con più formule. Pattern del massimo: conteggio di R3R_3 per scienziato, massimo dei conteggi, confronto. Risultato: Newton con 3 formule (1, 7, 9). Se due scienziati pareggiano, la query li restituisce entrambi. Con la vista Conteggio_F (conteggio per scienziato) la query è WHERE conteggio = (SELECT MAX(conteggio) FROM Conteggio_F). Per avere anche nome e cognome si raggruppa per s.id.

Errori del corso

  • Query 1: SQL senza DISTINCT, quindi la formula 6 compare due volte.
  • Query 2: confronto di campo (un id) con una stringa: manca il join con CAMPO_STUDIO, e manca DISTINCT.
  • Query 3: σ\sigma con una relazione al posto del join: è un'abbreviazione.

Domanda per la lode: attributo derivato n_formule in SCIENZIATO (con trigger) o vista materializzata per la query 3; indici su SCIENZIATO(cognome) e su R3(scienziato)R_3(\text{scienziato}); per la query 2 il nome del campo come chiave eviterebbe i due join, a costo di una chiave testuale ripetuta.

Errori tipici

  • Dimenticare DISTINCT con join che moltiplicano le righe.
  • Confrontare un id con un nome.
  • Usare una sola copia di CAMPO_STUDIO per due ruoli diversi.
  • σ\sigma con una relazione al posto di un valore.

Esercizi su questo argomento

Teoria collegata