← Torna al builder interattivo

Basi di Dati — Teoria

Cardinalità, traduzione al modello relazionale e forme normali — spiegati con esempi concreti.

Cardinalità e modello relazionale

La cardinalità di una relazione determina quante tabelle serviranno nel modello relazionale e dove andranno le chiavi esterne (FK).

1:1 Uno a uno

Ogni istanza di A è associata ad al più una di B, e viceversa.

Esempio: PERSONA e CARTA D'IDENTITÀ. Ogni persona ha una sola CI, ogni CI appartiene a una sola persona.

2 tabelle. La chiave primaria di una delle due entità entra come FK nell'altra. Di solito si sceglie il lato con partecipazione obbligatoria.
PERSONA
PK cod_fiscale
nome
cognome
FK num_ci
CARTA_IDENTITA
PK num_ci
data_rilascio
comune_rilascio
⚠️ Se la relazione ha attributi propri (es. data_emissione della CI che vale solo in relazione alla persona), conviene una 3ª tabella dedicata alla relazione.

1:N Uno a molti

Un'istanza di A è associata a molte di B; ogni B è associata ad al più una A.

Esempio: DOCENTE e CORSO. Un docente tiene molti corsi, ogni corso ha un solo docente titolare.

2 tabelle. La chiave primaria del lato "1" (DOCENTE) entra come FK nella tabella del lato "N" (CORSO).
DOCENTE
PK id_docente
nome
cognome
CORSO
PK id_corso
titolo
FK id_docente
⚠️ Caso con attributi di relazione: se vuoi registrare la data di inizio incarico di ogni docente per ogni corso, quella data non appartiene né al docente né al corso — appartiene alla relazione. In questo caso servono 3 tabelle, con una tabella INCARICO che contiene le FK di entrambi più la data.
DOCENTE
PK id_docente
nome
INCARICO
PKFK id_docente
PKFK id_corso
data_inizio
CORSO
PK id_corso
titolo

N:N Molti a molti

Un'istanza di A è associata a molte di B e viceversa.

Esempio: STUDENTE e CORSO. Uno studente segue molti corsi, un corso è seguito da molti studenti.

Sempre 3 tabelle. La relazione N:N non può essere rappresentata con solo 2 tabelle senza creare ridondanza. Serve sempre una tabella bridge che contiene le FK di entrambe le entità come chiave primaria composta.
STUDENTE
PK matricola
nome
cognome
ISCRIZIONE
PKFK matricola
PKFK id_corso
data_iscrizione
voto
CORSO
PK id_corso
titolo
crediti

Nota: la tabella bridge può contenere attributi propri della relazione (es. voto, data_iscrizione) che non appartengono né allo studente né al corso, ma alla coppia (studente, corso).

Quando la relazione ha attributi propri

Alcune proprietà non appartengono a una singola entità, ma alla relazione tra due entità. Questo è un punto critico che cambia la struttura del database.

Esempio — CLIENTE acquista PRODOTTO

La data di acquisto non è una proprietà del cliente (lo stesso cliente può acquistare in giorni diversi) né del prodotto (lo stesso prodotto può essere acquistato in giorni diversi da persone diverse).

La data di acquisto appartiene alla relazione ACQUISTO, cioè alla coppia (cliente, prodotto).

Regola pratica: chiediti sempre "questa proprietà cambia se cambia solo A? Cambia se cambia solo B?" — se la risposta è no in entrambi i casi, appartiene alla relazione, non alle entità.
Impatto sul numero di tabelle
Cardinalità Senza attr. di relazione Con attr. di relazione
1:1 2 tabelle (FK in una) 3 tabelle (tabella relazione)
1:N 2 tabelle (FK nel lato N) 3 tabelle (tabella relazione)
N:N 3 tabelle (bridge obbligatoria) 3 tabelle (bridge + attributi)
Come leggere il testo di un esercizio

Negli esercizi la cardinalità non viene mai data direttamente — devi ricavarla leggendo le frasi del testo. Ogni frase contiene indizi precisi sul minimo e sul massimo di partecipazione.

Metodo: per ogni associazione, leggi il testo in entrambe le direzioni e chiediti:
"Una singola istanza di A quante istanze di B può avere? Almeno quante? Al massimo quante?"
Poi ripeti nella direzione opposta.
Frasi tipiche e come tradurle
Frase nel testo min max Significato
"ogni studente deve seguire almeno un corso" 1 N partecipazione obbligatoria, molti corsi
"uno studente può non essere iscritto ad alcun corso" 0 N partecipazione opzionale, molti corsi
"ogni impiegato lavora in un solo ufficio" 1 1 obbligatorio, uno solo
"un ufficio può non avere impiegati assegnati" 0 N opzionale, molti impiegati
"ogni medico visita più pazienti" 1 N obbligatorio (almeno 1), molti pazienti
"ogni paziente ha esattamente un medico di base" 1 1 obbligatorio, uno solo
"un progetto può coinvolgere più dipendenti" 0 N opzionale (può anche averne zero), molti
Esempio completo — dal testo alla cardinalità

"Un docente può tenere più corsi, ma ogni corso è tenuto da esattamente un docente. Un corso deve avere almeno uno studente iscritto; uno studente può essere iscritto a più corsi oppure a nessuno."

AssociazioneDirezioneminmaxFrase che lo dice
DOCENTE — CORSO DOCENTE → CORSO 1N "può tenere più corsi"
CORSO → DOCENTE 11 "esattamente un docente"
STUDENTE — CORSO CORSO → STUDENTE 1N "almeno uno studente iscritto"
STUDENTE → CORSO 0N "a più corsi oppure a nessuno"

Risultato:

DOCENTE — CORSO: cardinalità 1:N → 2 tabelle, FK id_docente in CORSO

STUDENTE — CORSO: cardinalità N:N → 3 tabelle, bridge ISCRIZIONE

Attenzione alle parole ambigue: "può" indica quasi sempre min=0 (opzionale). "Deve", "almeno uno", "esattamente" indicano min=1 (obbligatorio). "Solo", "unico", "uno" indicano max=1. "Più", "molti", "diversi" indicano max=N.
Forme normali

Le forme normali sono regole progressive per eliminare ridondanze e anomalie dalle tabelle. Si applicano dopo aver tradotto lo schema E-R in tabelle relazionali.

Cos'è un'anomalia? È un problema che nasce dalla ridondanza dei dati:
Anomalia di inserimento — per inserire un dato devo conoscerne altri che non ho ancora
Anomalia di aggiornamento — per cambiare un dato devo modificarlo in più righe
Anomalia di cancellazione — eliminando una riga perdo informazioni che non volevo perdere

Partiamo da questa tabella volutamente sbagliata — la stessa che normalizzeremo passo per passo:

id_ordine cliente telefono prodotti città cap
1Mario Rossi333-111 iPhone, AirPods Palermo90100
2 Mario Rossi 333-111 iPad Palermo 90100
3Luca Bianchi347-222iPhoneRoma00100

In rosso: i valori problematici — liste in una cella e dati ripetuti inutilmente.

1
Prima Forma Normale (1NF)
Ogni cella deve contenere un solo valore atomico
Regola 1NF: ogni attributo deve contenere un valore atomico (non divisibile). Non sono ammesse liste, insiemi o gruppi di valori in una singola cella.

Problema nella nostra tabella: la colonna prodotti contiene "iPhone, AirPods" — due valori in una cella. Non posso cercare facilmente tutti gli ordini che contengono iPhone, non posso contare i prodotti, non posso gestirli separatamente.

❌ Prima della 1NF — lista in una cella
id_ordineclienteprodotti
1Mario RossiiPhone, AirPods
✓ Dopo la 1NF — una riga per ogni prodotto
id_ordineclientetelefonoprodottocittàcap
1Mario Rossi333-111iPhonePalermo90100
1Mario Rossi333-111AirPodsPalermo90100
2Mario Rossi333-111iPadPalermo90100
3Luca Bianchi347-222iPhoneRoma00100

Ora ogni cella ha un solo valore. Ma sorge un problema: la chiave primaria non può più essere solo id_ordine, perché le prime due righe hanno entrambe id_ordine = 1. Due righe con la stessa chiave non si possono distinguere.

Per identificare univocamente ogni riga servono entrambi i campi insieme: id_ordine + prodotto. La riga (1, iPhone) è diversa dalla riga (1, AirPods). Quindi la chiave primaria diventa la coppia (id_ordine, prodotto) — si chiama chiave primaria composta.

⚠️ Tienilo a mente: la chiave primaria è ora composta da due campi: (id_ordine, prodotto). Questo sarà fondamentale per capire la 2NF.

Rimane però un problema evidente: telefono, città e cap di Mario Rossi compaiono tre volte. Se Mario cambia numero di telefono, devo aggiornarlo in tre righe diverse — anomalia di aggiornamento. Questo ci porta alla 2NF.

Qual è la differenza tra 2NF e 3NF?

Entrambe parlano di "dipendenze" ma il problema è diverso:

2NF Problema con la chiave

Un dato dipende solo da una parte della chiave primaria composta. Il problema coinvolge sempre la chiave.

Nel nostro esempio: il telefono dipende solo da id_ordine, non dalla chiave intera (id_ordine, prodotto).

3NF Problema tra campi normali

Un dato dipende da un altro campo che non è la chiave. La chiave non c'entra — il problema è tra due campi normali.

Nel nostro esempio: il CAP dipende dalla città — entrambi sono campi normali, nessuno dei due è la chiave.

In breve: 2NF = "questo dato dipende da metà della chiave"  |  3NF = "questo dato non dipende dalla chiave per niente — dipende da un altro dato"
2
Seconda Forma Normale (2NF)
Per trovare ogni dato devi usare tutta la chiave, non solo una parte

Ricorda: la nostra chiave primaria è composta da (id_ordine, prodotto). In teoria, per trovare qualsiasi dato in questa tabella dovresti conoscere entrambi.

Ma proviamo a fare una domanda: qual è il telefono dell'ordine 1?

Per rispondere ti basta sapere che l'id_ordine è 1 — non ti serve sapere se ha preso un iPhone o un AirPod. Il numero di telefono è 333-111 in entrambi i casi.

Questo significa che il telefono lo trovi con solo una parte della chiave (id_ordine), senza usare l'altra parte (prodotto). Lo stesso vale per cliente, città e cap.

❌ Problema: per sapere il telefono ti basta l'id_ordine — non serve sapere anche quale prodotto è stato ordinato. Quindi il telefono non dovrebbe stare in una tabella che ha una chiave composta da (id_ordine, prodotto). Questa situazione si chiama dipendenza parziale: un dato che per essere trovato ha bisogno solo di una parte della chiave.

Soluzione: tutto ciò che riguarda il cliente — nome, telefono, città — va spostato in una tabella CLIENTE separata, con la sua chiave propria (id_cliente). Nella tabella degli ordini rimane solo il riferimento all'id_cliente.

✓ Dopo la 2NF — i dati del cliente stanno in una tabella propria
ORDINE_PRODOTTO
PK id_ordine
PK prodotto
FK id_cliente
CLIENTE
PK id_cliente
nome
telefono
città
cap

Ora telefono e città compaiono una sola volta per ogni cliente. Se Mario cambia numero di telefono, lo aggiorno in un posto solo. Rimane però un altro problema nella tabella CLIENTE: il cap dipende dalla città — non direttamente dall'id_cliente. Questo ci porta alla 3NF.

3
Terza Forma Normale (3NF)
Nessun attributo non-chiave dipende da un altro attributo non-chiave

Nella tabella CLIENTE abbiamo ancora un problema. Proviamo a fare una domanda: qual è il CAP di Palermo?

Per rispondere non ti serve sapere l'id_cliente — ti basta sapere la città. Il CAP 90100 lo trovi da "Palermo", non da "id_cliente = 1". Significa che il CAP non dipende dalla chiave della tabella (id_cliente), ma da un altro campo non-chiave (città).

Questa situazione si chiama dipendenza transitiva: per arrivare al CAP fai due passi — prima dall'id_cliente trovi la città, poi dalla città trovi il CAP. Come una catena: id_cliente → città → CAP.

❌ Problema: per sapere il CAP non ti serve l'id_cliente — ti basta la città. Se cambia il CAP di Palermo devo aggiornarlo in tutte le righe dei clienti palermitani — anomalia di aggiornamento.

Soluzione: città e CAP vanno in una tabella CITTA separata. Nella tabella CLIENTE rimane solo il riferimento alla città tramite id_citta.

✓ Dopo la 3NF — città e CAP stanno in una tabella propria
CLIENTE
PK id_cliente
nome
telefono
FK id_citta
CITTA
PK id_citta
nome_citta
cap
✓ Ora il database è in 3NF — nessuna ridondanza, nessuna anomalia.

Schema finale completo:

ORDINE_PRODOTTO
PK id_ordine
PK prodotto
FK id_cliente
CLIENTE
PK id_cliente
nome
telefono
FK id_citta
CITTA
PK id_citta
nome_citta
cap

Riepilogo delle forme normali

FormaCosa eliminaCondizione
1NF Valori non atomici (liste in una cella) Ogni cella ha un solo valore
2NF Dipendenze parziali (da parte della PK composta) Ogni attributo dipende dall'intera PK
3NF Dipendenze transitive (attributo → attributo) Nessun attributo non-chiave dipende da un altro non-chiave

Esempi concreti

FormaEsempio del problemaDove sta il problemaCome si risolve
1NF La cella prodotti contiene "iPhone, AirPods" — una lista di due valori in un campo solo In una cella con più valori Una riga separata per ogni prodotto: (ordine 1, iPhone) e (ordine 1, AirPods)
2NF Chiave: (id_ordine, prodotto). Per sapere il telefono ti basta id_ordine — non serve anche il prodotto Un dato dipende da metà della chiave composta Tabella CLIENTE separata con chiave id_cliente
3NF Chiave: id_cliente. Per sapere il CAP non ti serve id_cliente — ti basta la città. Catena: id_cliente → città → CAP Un dato dipende da un altro campo normale, non dalla chiave Tabella CITTA separata con chiave id_citta