Dopo la definizione di tutte le tabelle e dei loro attributi nel modello relazionale, bisogna analizzare il database per renderlo più piccolo e sicuro possibile, bisogna cioè "normalizzare" larchivio.
Vi sono vari livelli si normalizzazione, la prima forma normale, la seconda, la terza, quella di Boyce-Codd e così via. Ogni grado è una raffinazione del precedente, in una scala che porta via via ad archivi sempre più ridotti e tali che non sia possibile avere delle configurazioni di dati logicamente errate.
Ma ogni livello di normalizzazione può portare degli appesantimenti delle applicazioni che usufruiscono del database, per cui è lasciata al progettista la responsabilità di decidere il grado di normalizzazione da raggiungere caso per caso, tabella per tabella.
Vediamo ora i primi tre gradi di forma normale, i più utili e fondamentali per ogni database, indipendentemente dallapplicazione.
Un archivio si dice in prima forma normale (1NF) se non contiene alcun attributo ripetuto allinterno di ogni tabella.
Questo requisito è già specificato allinterno del modo di definire le tabelle e gli attributi nel modello relazionale, in pratica ogni database relazionale dovrebbe già nascere in prima forma normale; ma esplicitiamo questo concetto con un esempio.
Prendiamo ancora larchivio ordini. Per ogni ordine possono essere indicati più articoli, per cui potremmo essere tentati di dare uno schema di questo tipo:
ORDINI(COD_ORDINE,DATA,COD_CLI,COD_ART1,COD_ART2,COD_ART3)
cioè di memorizzare gli articoli ordinati in una serie finita di campi contenuti allinterno della tabella ORDINI.
Basta pochissima esperienza per capire come questo schema sia pericoloso: ogni ordine può memorizzare al massimo tre articoli, costringendoci a memorizzare due o più ordini al crescere degli articoli ordinati, e per contro occupa spazio inutile quando gli articoli sono meno di tre.
Per normalizzare larchivio bisogna spezzare la tabella ORDINI in due tabelle, una per le testate e una per le righe di ordine.
ORDINI(COD_ORDINE,DATA,COD_CLI)
ORDINI_RIGHE(COD_ORDINE,COD_ART,QTA)
La regola di togliere tutti i campi ripetuti creando una nuova tabella non può però essere presa come assoluta: ci sono dei casi in cui è proprio il significato dellentità a richiedere la ripetizione di un campo.
In un archivio di partite di calcio, è naturale che il campo SQUADRA comparirà due volte, perchè è proprio il significato di PARTITA a richiedere il nome dei due contendenti.
Il progettista deve saper valutare se il significato dei dati obbliga o meno alla ripetizione dei campi, e separare in due tabelle tutte le volte in cui non sia chiarissimo che i campi hanno senso solo se uniti allinterno dello stesso archivio.
Un archivio è in seconda forma normale (2NF) se è in prima forma normale (1NF) e ogni campo che non fa parte della chiave dipende funzionalmente dalla chiave.
Riprendiamo lesempio degli ordini. Potremo estendere le due tabelle in questo modo:
ORDINI(COD_ORDINE,DATA,COD_CLI,INDIR_CLI,CITTA_CLI)
ORDINI_RIGHE(COD_ORDINE,COD_ART,DES_ART,PRZ_ART,QTA)
Iin modo da contenere i dati dei clienti e degli articoli.
E abbastanza semplice vedere che il campo INDIR_CLI non è indipendente dal campo COD_CLI! Esiste cioè una dipendenza funzionale tra il codice del cliente, che identifica un ben preciso soggetto, ed il suo indirizzo. La stessa considerazione vale per il campo CITTA_CLI, ma anche per DES_ART e PRZ_ART contenuti nella tabella ORDINI_RIGHE.
Questa configurazione porta a parecchi problemi:
Non è possibile introdurre CLIENTI o ARTICOLI nellarchivio finchè non sono introdotti degli ordini.
Se vogliamo cambiare lindirizzo di un cliente dobbiamo scorrere tutti i record dellarchivio degli ordini per aggiornare ogni rifermento, altrimenti troviamo un archivio logicamente errato perchè un cliente è associato a uno o più indirizzi.
Appena cancelliamo tutti i riferimenti ad un cliente o ad un articolo, questi spariscono completamente dallarchivio.
Per risolverli dobbiamo spezzare le tabelle introducendo due nuovi archivi.
ORDINI(COD_ORDINE,DATA,COD_CLI)
ORDINI_RIGHE(COD_ORDINE,COD_ART)
CLIENTI(COD_CLI,INDIR_CLI,CITTA_CLI)
ARTICOLI(COD_ART,DES_ART,PRZ_ART,QTA)
In questo modo tutti i campi di ogni archivio dipendono funzionalmente solo dalla chiave e non abbiamo i problemi evidenziati per larchivio solo 1NF.
Perché larchivio sia in terza forma normale, deve essere 2NF e non deve esistere nessuna dipendenza funzionale transitiva.
Dopo aver effettuato la seconda normalizzazione abbiamo spostato tutti i dati che non dipendono dalla chiave primaria in altre tabelle collegate.
Ora possono esistere dei dati che oltre a dipendere dalla chiave primaria, dipendono anche da un altro campo. Per portarci in terza forma normale dobbiamo spostare anche questi campi.
Vediamo ancora con un esempio: nellarchivio clienti avremo lindirizzo, la città e il codice postale.
Questo archivio non è completamente normalizzato, perchè la città dipende dal codice postale; in questo caso cioè abbiamo dei campi che dipendono tutti dal codice cliente e uno che dipende anche da un altro campo in modo transitivo.
Per portarci in terza forma normale dovremo lasciare solo il codice postale nellarchivio clienti e spostare la città in una tabella dei CAP.
Possiamo avere delle dipendenze che non portano necessariamente a spostare gli attributi e a creare nuovi archivi; alcuni campi possono dipendere da altri per dei semplici calcoli.
Per esempio un totale dellordine può essere memorizzato, ma anche calcolato sommando la quantità di ogni riga.
Lesperienza indica che un tracciato in terza forma normale è sufficientemente stabile rispetto ai cambiamenti che possono essere introdotti durante la vita di un archivio e garantisce la massima sicurezza logica dei dati rispetto alle operazioni di inserimento e cancellazione dei dati.
Daltra parte il processo di normalizzazione porta alla creazione di tanti piccoli archivi collegati tramite dei riferimenti a chiavi, che nelluso pratico rendono difficoltose e rallentano le operazioni di ricerca.
Il progettista deve sempre avere bene in mente il significato di ogni dato che viene inserito nel database, mediando le esigenze logiche di pulizia e correttezza che spingono verso una forte normalizzazione con quelle fisiche di velocità e non frammentazione degli archivi che vorrebbero una base di dati meno normalizzata, per giungere a dei compromessi che garantiscano entrambe gli aspetti.