Lezioni del modulo (2/4)
Vincoli (Constraints) Multipli e Tabellari
Garantire che i dati nel database siano corretti allo strato più base ("data integrity") è fondamentale. Se la sicurezza la scrivi solo nell'applicazione NodeJS, i bug potrebbero causare dati inconsistenti nel DB. I vincoli (Constraints) a livello DDL sono l'ultima, impenetrabile linea di difesa.
I principali vincoli
Oltre a PRIMARY KEY (che rende una colonna univoca e NOT NULL) e ai classici NOT NULL e UNIQUE, esistono due categorie fondamentali per la consistenza logica del dato:
1. FOREIGN KEY (Integrità Referenziale)
Un vincolo REFERENCES assicura che un ID di collegamento esista davvero nella tabella "padre" in cui punta.
Ad esempio, un ordine deve essere collegato a un cliente esistente.
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer_id INTEGER REFERENCES customers(id)
);Cosa fa la Foreign Key?
- Ti impedisce di inserire un
customer_idche non esiste incustomers. - Ti impedisce di
DELETE(eliminare) un cliente dacustomersse ci sono ordini che puntano a lui (protegge dai record "orfani").
Possiamo anche decidere cosa deve succedere quando il "padre" viene cancellato:
customer_id INTEGER REFERENCES customers(id) ON DELETE CASCADECon ON DELETE CASCADE, se cancelliamo il cliente, Postgres eliminerà automaticamente tutti gli orders collegati! (Da usare con grande cautela, di norma è meglio l'impostazione di base che blocca l'operazione).
2. CHECK Constraints
Un vincolo CHECK convalida la riga testando una espressione booleana prima di procedere al salvataggio.
CREATE TABLE contratti (
id SERIAL PRIMARY KEY,
stipendio NUMERIC(10,2) CHECK (stipendio > 0),
data_inizio DATE,
data_fine DATE,
CHECK (data_fine > data_inizio)
);Il primo CHECK è un vincolo di colonna (si riferisce solo a stipendio).
Il secondo CHECK in fondo è un vincolo di tabella (può incrociare i valori di più colonne, e va dichiarato in fondo).
Se un INSERT o UPDATE prova a salvare la fine prima dell'inizio, Postgres lo rifiuterà con errore!
Nomenclature dei Constraint
Spesso è buona pratica dare un nome esplicito ai constraint (usando la keyword CONSTRAINT). In questo modo, quando Postgres blocca un record, l'errore ti dirà ad esempio "violato il vincolo 'stipendio_positivo'", molto più chiaro per far debug!
CREATE TABLE prodotti (
id SERIAL PRIMARY KEY,
nome VARCHAR(50),
CONSTRAINT nome_univoco UNIQUE(nome)
);Prova tu
Crea una tabella 'prenotazioni' con id (SERIAL PRIMARY KEY) e un 'customer_id' (INTEGER). Usa CONSTRAINT fk_cliente FOREIGN KEY (customer_id) REFERENCES customers(id) per dare un nome esplicito al vincolo di Foreign Key. Fallo in fondo, come vincolo di tabella.
Mostra suggerimento
Scrivi CONSTRAINT fk_cliente FOREIGN KEY (colonna_locale) REFERENCES tabella_esterna(col_esterna)
Soluzione disponibile dopo 3 tentativi
Vincoli con logica custom
Crea una tabella 'eventi' con id (SERIAL PRIMARY KEY), posti_totali (INTEGER) e biglietti_venduti (INTEGER). Aggiungi un constraint di tabella chiamato 'check_capienza' dove i biglietti_venduti non devono mai essere maggiori dei posti_totali.
Mostra suggerimento
Aggiungi il vincolo in fondo usando CONSTRAINT check_capienza CHECK ( espressione )
Soluzione disponibile dopo 3 tentativi