Ogni tabella di un database relazionale dovrebbe avere una chiave primaria: una colonna, o un insieme di colonne, che identifica ogni riga in modo univoco e non può essere vuota. La domanda che si pone quasi sempre in fase di progettazione è se usare un dato che esiste già nel mondo reale, come un codice fiscale o un indirizzo email, oppure un identificativo creato apposta dal database. La scelta ha conseguenze che si scoprono spesso anni dopo.
Due famiglie di chiavi
Una chiave naturale è un attributo che ha già un significato al di fuori del database: il codice fiscale di una persona, la partita IVA di un'azienda, il codice ISBN di un libro, la targa di un veicolo, il codice IBAN di un conto.
Una chiave surrogata è un valore senza significato, generato dal sistema solo per identificare la riga: un intero progressivo (1, 2, 3…) o un identificativo universale (UUID).
I vantaggi delle chiavi naturali
Le chiavi naturali hanno un'attrattiva evidente:
- non serve una colonna in più, perché il dato si deve memorizzare comunque;
- impediscono di per sé i duplicati: due righe con lo stesso codice fiscale vengono rifiutate;
- rendono leggibili le tabelle collegate, perché nella tabella degli ordini si vede subito a chi si riferiscono.
I problemi delle chiavi naturali
Il difficile è che i dati del mondo reale cambiano più spesso di quanto si pensi, e una chiave primaria dovrebbe restare stabile per sempre.
Il valore può cambiare
Un indirizzo email si cambia di continuo. Anche un codice fiscale, che sembra immutabile, può essere rettificato se era stato calcolato con dati sbagliati. Quando una chiave cambia, il cambiamento va propagato a tutte le tabelle che la usano come riferimento: operazione possibile, ma rischiosa e lenta su grandi quantità di dati.
Il valore può mancare
Un cliente straniero può non avere un codice fiscale italiano. Un'azienda in fase di costituzione può non avere ancora una partita IVA. Se la chiave è obbligatoria, non si potrà registrare quel soggetto, oppure si inventeranno valori fittizi che sporcano i dati.
Il valore può non essere davvero unico
Molti identificativi «unici» lo sono solo in teoria. Esistono casi di omocodia nei codici fiscali, risolti con un meccanismo apposito, e non tutti i sistemi lo gestiscono correttamente. Gli identificativi assegnati da terzi possono anche essere riassegnati nel tempo.
Il valore può essere un dato personale
Usare il codice fiscale come chiave significa propagarlo in ogni tabella collegata, nei log, negli URL e nei file di esportazione. Ogni copia è un dato personale in più da proteggere.
I vantaggi delle chiavi surrogate
Una chiave surrogata non ha nessuno di questi problemi: non cambia mai, esiste sempre fin dalla creazione della riga, è unica per costruzione e non rivela nulla. Inoltre è compatta: un intero occupa pochi byte, e gli indici e le relazioni costruiti su di esso sono veloci.
CREATE TABLE clienti (
id_cliente BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
codice_fiscale CHAR(16) UNIQUE,
email TEXT NOT NULL UNIQUE,
nome TEXT NOT NULL
);
In questo esempio la chiave primaria è surrogata, ma il codice fiscale e l'email restano protetti da un vincolo UNIQUE. È il punto più importante: una chiave surrogata non elimina la necessità di vincoli di unicità sui dati reali. Senza il vincolo, nulla impedisce di inserire lo stesso cliente due volte con due id diversi.
Interi progressivi o UUID
Tra le chiavi surrogate ci sono due scelte principali.
| Aspetto | Intero progressivo | UUID |
|---|---|---|
| Dimensione | 4 o 8 byte | 16 byte |
| Leggibilità | Semplice da dettare e confrontare | Lungo e scomodo da leggere |
| Generazione | Dal database, in sequenza | Ovunque, anche offline o lato applicazione |
| Prevedibilità | Si indovina l'id successivo | Non prevedibile |
| Unione di database diversi | Collisioni probabili | Collisioni praticamente impossibili |
Un intero progressivo è la scelta più semplice per la maggior parte delle applicazioni. Gli UUID sono utili quando più sistemi creano record in modo indipendente, quando si devono unire dati provenienti da fonti diverse o quando l'id compare in un indirizzo web e non si vuole che qualcuno possa scorrere i record cambiando un numero. Va però ricordato che nascondere l'id non sostituisce un controllo dei permessi: ogni richiesta deve comunque verificare che l'utente possa vedere quel record.
Gli UUID completamente casuali, inseriti in un indice ordinato, causano scritture sparse in punti diversi dell'indice. Le versioni ordinate nel tempo, come gli UUID versione 7, attenuano questo problema.
Quando la chiave naturale va bene
Esistono casi in cui una chiave naturale è la scelta più sensata:
- tabelle di codifica stabili, come i codici ISO dei paesi o delle valute, che cambiano raramente e sono standard;
- tabelle ponte delle relazioni molti a molti, dove la chiave composta dalle due chiavi esterne è naturale e sufficiente;
- dati interni il cui codice è deciso e controllato dalla stessa organizzazione e non verrà mai riassegnato.
In sintesi
- La chiave primaria deve essere unica, sempre presente e stabile nel tempo.
- Le chiavi naturali sono comode ma possono cambiare, mancare o contenere dati personali.
- Le chiavi surrogate sono stabili e compatte, ma vanno affiancate da vincoli
UNIQUEsui dati reali. - Interi progressivi per la maggior parte dei casi, UUID per sistemi distribuiti o id esposti.
- Le chiavi naturali restano adatte a codifiche standard e tabelle ponte.