▦ Dati e database

Normalizzazione del database: le prime tre forme normali

Prima, seconda e terza forma normale spiegate con un esempio di ordini e clienti: quali problemi risolvono e quando ha senso denormalizzare.

La normalizzazione è il procedimento con cui si organizza un database relazionale in modo che ogni informazione sia memorizzata una sola volta e nel posto giusto. Il termine suona accademico, ma l'obiettivo è molto pratico: evitare che inserimenti, modifiche e cancellazioni producano dati contraddittori. Il modo migliore per capirla è partire da una tabella fatta male e sistemarla un passo alla volta.

La tabella di partenza

Immaginiamo un piccolo negozio online che registra tutto in un'unica tabella:

ordine data cliente email_cliente città_cliente prodotti
101 2025-03-02 Rossi rossi@esempio.it Torino Penna x2, Quaderno x1
102 2025-03-02 Bianchi bianchi@esempio.it Bari Quaderno x3
103 2025-03-05 Rossi rossi@esempio.it Torino Zaino x1

Funziona finché i dati sono pochi, ma nasconde tre tipi di problemi, chiamati anomalie.

  • Anomalia di aggiornamento: se Rossi cambia email, va modificata su ogni suo ordine. Dimenticarne uno crea un'incoerenza.
  • Anomalia di inserimento: non si può registrare un nuovo cliente finché non ha fatto un ordine, perché ogni riga è un ordine.
  • Anomalia di cancellazione: se si elimina l'unico ordine di Bianchi, si perde anche l'informazione che Bianchi esiste e abita a Bari.

Prima forma normale (1NF)

Una tabella è in prima forma normale quando ogni cella contiene un valore atomico, cioè uno solo, e non ci sono gruppi ripetuti. La colonna «prodotti» viola questa regola: contiene un elenco, per giunta con le quantità mescolate al nome.

Per sistemarla si crea una riga per ogni prodotto ordinato:

ordine data cliente email_cliente città_cliente prodotto quantità
101 2025-03-02 Rossi rossi@esempio.it Torino Penna 2
101 2025-03-02 Rossi rossi@esempio.it Torino Quaderno 1

Ora si può contare quante penne sono state vendute con una semplice query, cosa impossibile quando i prodotti erano testo libero. La chiave della tabella diventa la coppia (ordine, prodotto).

Seconda forma normale (2NF)

La seconda forma normale riguarda le tabelle con chiave composta. Richiede che ogni colonna non chiave dipenda dall'intera chiave, non da una sua parte.

Nella tabella precedente la quantità dipende da ordine e prodotto insieme: va bene. Ma data, cliente, email e città dipendono solo dal numero d'ordine. Sono ripetute per ogni riga di prodotto senza motivo.

La soluzione è separare:

  • ordini (ordine, data, cliente, email_cliente, città_cliente)
  • righe_ordine (ordine, prodotto, quantità)

Allo stesso modo, se avessimo il prezzo di listino del prodotto nella tabella delle righe, andrebbe spostato in una tabella prodotti, perché dipende solo dal prodotto.

Terza forma normale (3NF)

La terza forma normale elimina le dipendenze transitive: una colonna non chiave non deve dipendere da un'altra colonna non chiave. Nella tabella ordini, email e città non dipendono dall'ordine in sé, ma dal cliente. L'ordine determina il cliente, e il cliente determina email e città.

Si crea quindi una tabella clienti e la tabella ordini conserva solo un riferimento:

CREATE TABLE clienti (
  id_cliente  INTEGER PRIMARY KEY,
  nome        TEXT NOT NULL,
  email       TEXT NOT NULL UNIQUE,
  citta       TEXT
);

CREATE TABLE ordini (
  id_ordine   INTEGER PRIMARY KEY,
  data_ordine DATE NOT NULL,
  id_cliente  INTEGER NOT NULL REFERENCES clienti(id_cliente)
);

CREATE TABLE righe_ordine (
  id_ordine   INTEGER REFERENCES ordini(id_ordine),
  id_prodotto INTEGER REFERENCES prodotti(id_prodotto),
  quantita    INTEGER NOT NULL CHECK (quantita > 0),
  PRIMARY KEY (id_ordine, id_prodotto)
);

Le tre anomalie sono sparite: l'email si cambia in un solo punto, un cliente può esistere senza ordini e cancellare un ordine non cancella il cliente.

Un modo semplice per ricordarle

Una formula spesso usata per riassumere le tre forme è che ogni attributo deve dipendere «dalla chiave, da tutta la chiave e da nient'altro che la chiave». La prima parte corrisponde alla 1NF, la seconda alla 2NF, la terza alla 3NF. Esistono forme normali successive, come la forma normale di Boyce-Codd, ma nella pratica quotidiana arrivare alla terza risolve la grande maggioranza dei problemi.

Quando denormalizzare

Normalizzare non significa frammentare tutto all'infinito. Ci sono casi in cui duplicare un dato è una scelta corretta:

  • Dati storici. Il prezzo pagato va salvato nella riga d'ordine anche se esiste nella tabella prodotti, perché il listino cambierà e l'ordine deve ricordare il prezzo di quel giorno. Non è una ridondanza: è un'informazione diversa.
  • Indirizzo di spedizione. Allo stesso modo, l'indirizzo usato per un ordine va congelato nell'ordine.
  • Report e analisi. Nei sistemi dedicati all'analisi si usano spesso tabelle volutamente denormalizzate per rendere le query più semplici e veloci.

La regola pratica è partire da un modello normalizzato e denormalizzare solo per un motivo preciso, documentandolo.

In sintesi

  • La normalizzazione elimina le anomalie di aggiornamento, inserimento e cancellazione.
  • 1NF: valori atomici, niente elenchi dentro una cella.
  • 2NF: nessuna colonna dipende solo da una parte della chiave composta.
  • 3NF: nessuna colonna dipende da un'altra colonna non chiave.
  • Si denormalizza solo per ragioni chiare, come conservare dati storici.
MM
Marco Marzaduri

Marco Marzaduri si occupa di cybersecurity, informatica forense e sviluppo web. Laureato in informatica e in psicologia, unisce l'analisi tecnica alla comprensione del fattore umano.

Leggi anche