La normalizzazione elimina ridondanze e anomalie di inserimento, cancellazione e aggiornamento. Vediamo le tre forme normali (1NF, 2NF, 3NF) con esempi reali e diagrammi illustrativi.
Tabella "Studenti" con numeri di telefono multipli nella stessa cella e attributi ripetuti.
| ID Studente | Nome | Telefoni | Telefono2 |
|---|---|---|---|
| 1 | Marco Rossi | 331123456, 331654321 | NULL |
| 2 | Laura Bianchi | 392111222 | 392333444 |
⚠️ Problemi: I telefoni non sono atomici (nel primo caso ci sono due numeri separati da virgola). Colonne Telefono1/Telefono2 creano ridondanza e difficoltà di ricerca.
Suddivido i telefoni su righe distinte e creo una chiave primaria composta (ID_Studente, Telefono).
| ID Studente | Nome | Telefono |
|---|---|---|
| 1 | Marco Rossi | 331123456 |
| 1 | Marco Rossi | 331654321 |
| 2 | Laura Bianchi | 392111222 |
| 2 | Laura Bianchi | 392333444 |
Tabella "IscrizioniCorsi" con chiave primaria composta (ID_Studente, ID_Corso).
| ID_Studente | ID_Corso | Nome_Studente | Nome_Corso | Data_Iscrizione |
|---|---|---|---|---|
| 1 | 101 | Marco | Matematica | 2025-01-10 |
| 1 | 102 | Marco | Fisica | 2025-01-12 |
| 2 | 101 | Laura | Matematica | 2025-01-11 |
⚠️ Problema: "Nome_Studente" dipende solo da ID_Studente (non da ID_Corso), e "Nome_Corso" dipende solo da ID_Corso. Sono dipendenze parziali → violazione 2NF. Ridondanza: "Marco" e "Matematica" vengono ripetuti più volte.
Creo tre tabelle separate, eliminando ogni dipendenza parziale:
📋 Tabella STUDENTI
| ID_Studente (PK) | Nome_Studente |
|---|---|
| 1 | Marco |
| 2 | Laura |
📋 Tabella CORSI
| ID_Corso (PK) | Nome_Corso |
|---|---|
| 101 | Matematica |
| 102 | Fisica |
📋 Tabella ISCRIZIONI (tabella ponte)
| ID_Studente (FK) | ID_Corso (FK) | Data_Iscrizione |
|---|---|---|
| 1 | 101 | 2025-01-10 |
| 1 | 102 | 2025-01-12 |
| 2 | 101 | 2025-01-11 |
Tabella "Impiegati" in 2NF ma con dipendenza transitiva.
| ID_Impiegato | Nome | ID_Ufficio | Città_Ufficio |
|---|---|---|---|
| 10 | Anna | U1 | Roma |
| 11 | Luigi | U2 | Milano |
| 12 | Sofia | U1 | Roma |
⚠️ Problema: "Città_Ufficio" dipende da "ID_Ufficio" (attributo non chiave), non direttamente da ID_Impiegato. Esiste una dipendenza transitiva:
ID_Impiegato → ID_Ufficio → Città_Ufficio.
Ridondanza: "Roma" ripetuta per ogni impiegato dello stesso ufficio.
Anomalie: se un ufficio cambia città, bisogna aggiornare tutte le righe degli impiegati (rischio inconsistenza).
Separare la dipendenza transitiva in una tabella dedicata agli uffici.
📋 Tabella IMPIEGATI (3NF)
| ID_Impiegato (PK) | Nome | ID_Ufficio (FK) |
|---|---|---|
| 10 | Anna | U1 |
| 11 | Luigi | U2 |
| 12 | Sofia | U1 |
📋 Tabella UFFICI
| ID_Ufficio (PK) | Città_Ufficio |
|---|---|
| U1 | Roma |
| U2 | Milano |
| Forma normale | Regola chiave | Problema risolto | Quando applicare |
|---|---|---|---|
| 1NF | Attributi atomici + chiave univoca | Dati multivalore, gruppi ripetuti | Sempre |
| 2NF | Nessuna dipendenza parziale | Ridondanza in chiave composta | Con chiavi primarie composte |
| 3NF | Nessuna dipendenza transitiva | Ridondanza tra attributi non chiave | Quasi sempre (fino a 3NF è standard) |
Di seguito trovi 4 esercizi di difficoltà crescente. Prova a normalizzare i dati fino alla 3NF, poi confronta con la soluzione.
Consegna: La tabella "Libri" memorizza informazioni sui libri e i loro autori. Normalizza in 1NF.
| ID_Libro | Titolo | Autori | Anno |
|---|---|---|---|
| 1 | Database Facile | Rossi, Bianchi | 2020 |
| 2 | SQL Avanzato | Verdi | 2021 |
| 3 | NoSQL Guida | Neri, Rossi, Gialli | 2022 |
Soluzione: Scomporre gli autori su righe separate. Chiave primaria composta (ID_Libro, Autore).
| ID_Libro | Titolo | Autore | Anno |
|---|---|---|---|
| 1 | Database Facile | Rossi | 2020 |
| 1 | Database Facile | Bianchi | 2020 |
| 2 | SQL Avanzato | Verdi | 2021 |
| 3 | NoSQL Guida | Neri | 2022 |
| 3 | NoSQL Guida | Rossi | 2022 |
| 3 | NoSQL Guida | Gialli | 2022 |
✅ Ora ogni cella contiene un valore atomico.
Consegna: La tabella "OrdiniClienti" ha chiave primaria composta (ID_Ordine, ID_Prodotto). Normalizza in 2NF.
| ID_Ordine | ID_Prodotto | Data_Ordine | Nome_Cliente | Prodotto_Nome | Quantità |
|---|---|---|---|---|---|
| 101 | P1 | 2025-01-10 | Marco | Mouse | 2 |
| 101 | P2 | 2025-01-10 | Marco | Tastiera | 1 |
| 102 | P1 | 2025-01-11 | Laura | Mouse | 3 |
Soluzione: Identificare le dipendenze parziali: Data_Ordine e Nome_Cliente dipendono solo da ID_Ordine. Prodotto_Nome dipende solo da ID_Prodotto. Creare 3 tabelle.
Tabella ORDINI:
| ID_Ordine (PK) | Data_Ordine | Nome_Cliente |
|---|---|---|
| 101 | 2025-01-10 | Marco |
| 102 | 2025-01-11 | Laura |
Tabella PRODOTTI:
| ID_Prodotto (PK) | Prodotto_Nome |
|---|---|
| P1 | Mouse |
| P2 | Tastiera |
Tabella DETTAGLI_ORDINE (tabella ponte):
| ID_Ordine (FK) | ID_Prodotto (FK) | Quantità |
|---|---|---|
| 101 | P1 | 2 |
| 101 | P2 | 1 |
| 102 | P1 | 3 |
✅ Eliminate tutte le dipendenze parziali.
Consegna: La tabella "ProgettiDipendenti" è già in 2NF, ma ha una dipendenza transitiva. Normalizza in 3NF.
| ID_Dipendente | Nome_Dipendente | ID_Progetto | Nome_Progetto | Reparto | Edificio_Reparto |
|---|---|---|---|---|---|
| D1 | Anna | PRJ1 | AI | R&D | Edificio A |
| D2 | Luigi | PRJ1 | AI | R&D | Edificio A |
| D3 | Sofia | PRJ2 | Cloud | Infrastrutture | Edificio B |
Suggerimento: Individua la dipendenza transitiva: ID_Dipendente → ? → ?
Soluzione: Dipendenza transitiva: ID_Dipendente → Reparto → Edificio_Reparto. Inoltre Nome_Progetto dipende da ID_Progetto (altro attributo non chiave).
Tabella DIPENDENTI_PROGETTI (relazione pura):
| ID_Dipendente | ID_Progetto |
|---|---|
| D1 | PRJ1 |
| D2 | PRJ1 |
| D3 | PRJ2 |
Tabella DIPENDENTI:
| ID_Dipendente (PK) | Nome_Dipendente | Reparto |
|---|---|---|
| D1 | Anna | R&D |
| D2 | Luigi | R&D |
| D3 | Sofia | Infrastrutture |
Tabella REPARTI:
| Reparto (PK) | Edificio_Reparto |
|---|---|
| R&D | Edificio A |
| Infrastrutture | Edificio B |
Tabella PROGETTI:
| ID_Progetto (PK) | Nome_Progetto |
|---|---|
| PRJ1 | AI |
| PRJ2 | Cloud |
✅ Eliminate tutte le dipendenze transitive.
Consegna: La tabella "CorsiStudentiDocenti" riassume corsi, studenti e docenti. Normalizza completamente fino alla 3NF.
| ID_Corso | Nome_Corso | Studenti (lista) | ID_Docente | Nome_Docente | Specializzazione_Docente |
|---|---|---|---|---|---|
| CS101 | Database | Marco, Laura | D10 | Rossi | SQL, NoSQL |
| CS102 | Python | Marco, Sofia, Luigi | D20 | Bianchi | Python, Django |
| CS101 | Database | Sofia | D10 | Rossi | SQL, NoSQL |
Soluzione passo passo:
Tabella CORSI:
| ID_Corso (PK) | Nome_Corso |
|---|---|
| CS101 | Database |
| CS102 | Python |
Tabella DOCENTI:
| ID_Docente (PK) | Nome_Docente |
|---|---|
| D10 | Rossi |
| D20 | Bianchi |
Tabella SPECIALIZZAZIONI_DOCENTE (1NF per le specializzazioni):
| ID_Docente (FK) | Specializzazione |
|---|---|
| D10 | SQL |
| D10 | NoSQL |
| D20 | Python |
| D20 | Django |
Tabella STUDENTI (dall'estrazione degli studenti unici):
| ID_Studente (generato) | Nome_Studente |
|---|---|
| S1 | Marco |
| S2 | Laura |
| S3 | Sofia |
| S4 | Luigi |
Tabella ISCRIZIONI_CORSI (relazione studenti-corsi):
| ID_Studente (FK) | ID_Corso (FK) |
|---|---|
| S1 | CS101 |
| S2 | CS101 |
| S1 | CS102 |
| S3 | CS102 |
| S4 | CS102 |
| S3 | CS101 |
Tabella ASSEGNAZIONE_DOCENTI (un corso può avere un docente):
| ID_Corso (FK) | ID_Docente (FK) |
|---|---|
| CS101 | D10 |
| CS102 | D20 |
✅ Database completamente normalizzato in 3NF, senza ridondanze né anomalie.
Dopo aver completato gli esercizi, verifica di aver compreso:
📚 Esercizi tratti da casi reali - Prova a inventare nuovi esempi dalla tua esperienza per allenarti ulteriormente!