Howto Come forzare l'uso delle FK con Sqlite
Da PtLUG Wiki.
Sqlite fornisce la sintassi per la creazione di Foreign Key.
Tuttavia allo stato attuale non esegue questa restrizione risultanto pertanto possibile inserire dati non validi all'interno delle tabelle che dovrebbero, invece, essere vincolate.
Per raggirare questo limitazione è possibile far uso dei trigger.
Contents |
Creazione delle tabelle
Andiamo a fare un piccolo esempio che ci sarà utile per mostrare il nostro espediente: Immaginiamo di dover immagazzinare gli articoli delle nostre riviste preferite e di volere utilizzare solamente due tabelle:
- RIVISTA: contiene l'ID e il nome della rivista.
- ARTICOLO: contiene oltre al suo id l'id della rivista alla quale l'articolo appartiene, il titolo e la pagina in cui l'articolo inizia.
CREATE TABLE ARTICOLO ( ID INTEGER NOT NULL PRIMARY KEY, ID_RIVISTA INTEGER NOT NULL CONSTRAINT fk_rivista_id REFERENCES RIVISTA(ID) ON DELETE CASCADE, TITOLO VARCHAR(200) NOT NULL, PAG INTEGER NOT NULL, UNIQUE(ID_RIVISTA,TITOLO,PAG) );
CREATE TABLE RIVISTA ( ID INTEGER NOT NULL PRIMARY KEY, NOME VARCHAR(50) NOT NULL, unique(NOME) );
Inserimento dei dati
Iniziamo a popolare la tabella Rivista:
insert into RIVISTA values (null,'Linux & C.'); insert into RIVISTA values (null,'Linu Magazine');
Abbiamo immesso NULL al posto di ID per far sì che sia Sqlite ad occuparsi, in automatico, di aggiornare tale valore (si tratta infatti di un campo di PK).
La tabella RIVISTA sarà adesso così costituita:
ID NOME -- -------------- 1 Linux & C. 2 Linux Magazine
Bene andiamo adesso a popolare la tabella degli articoli:
insert into ARTICOLO values (null,1,'Articolo numero uno',30); insert into ARTICOLO values (null,1,'Articolo numero due',40); insert into ARTICOLO values (null,2,'Articolo numero tre',10); insert into ARTICOLO values (null,2,'Articolo numero quattro',11);
Proviamo a mettere adesso un id_rivista non esistente:
insert into ARTICOLO values (null,3,'Articolo numero cinque',65);
...Caspita!!! Nessun errore. Sqlite ci ha inserito un articolo senza avere la corrispondente rivista. E questo non va bene, abbiamo infatti volutamente violato il vincolo della Foreign Key. Sqlite, infatti, consente di definire le FK ma non ci obbliga a rispettarlo. In sostanza è come se non ci fosse.
Sappiamo tuttavia che in Sqlite è possibile scrivere dei trigger... perchè non usarli per forzare noi il rispetto di queste regole sul nostro database? Innanzitutto rimuoviamo il record che viola la FK:
delete from articolo where id=5;
FK sfruttando i trigger
Quindi procediamo a scrivere le nostre poche righe di istruzione per i trigger:
-- CREAZIONE TRIGGER DELETE SULLA TABELLA RIVISTA -- a cascata andiamo ad eliminare tutti i record della -- tabella Articolo correlati CREATE TRIGGER fkdc_ARTICOLO_ID_RIVISTA_RIVISTA_ID BEFORE DELETE ON RIVISTA FOR EACH ROW BEGIN DELETE FROM ARTICOLO WHERE ARTICOLO.ID_RIVISTA = OLD.ID; END;
-- CREAZIONE TRIGGER INSERT SULLA TABELLA ARTICOLO -- Prima dell'inserimento di un articolo andiamo a controllare -- se esiste la rivista collegata altrimenti alziamo un eccezione CREATE TRIGGER fki_ARTICOLO_ID_RIVISTA_RIVISTA_ID BEFORE INSERT ON [ARTICOLO] FOR EACH ROW BEGIN SELECT RAISE(ROLLBACK, 'insert on table "ARTICOLO" violates foreign key constraint "fki_ARTICOLO_ID_RIVISTA_RIVISTA_ID"') WHERE (SELECT ID FROM RIVISTA WHERE ID = NEW.ID_RIVISTA) IS NULL; END;
-- CREAZIONE TRIGGER UPDATE SULLA TABELLA ARTICOLO
-- Prima dell'aggiornamento di un articolo andiamo a controllare
-- se esiste la rivista collegata altrimenti alziamo un eccezione
CREATE TRIGGER fku_ARTICOLO_ID_RIVISTA_RIVISTA_ID
BEFORE UPDATE ON [ARTICOLO]
FOR EACH ROW BEGIN
SELECT RAISE(ROLLBACK, 'update on table "ARTICOLO" violates foreign key constraint "fku_ARTICOLO_ID_RIVISTA_RIVISTA_ID"')
WHERE (SELECT ID FROM RIVISTA WHERE ID = NEW.ID_RIVISTA) IS NULL;
END;
Proviamo adesso nuovamente ad inserire un articolo che viola la FK:
insert into ARTICOLO values (null,3,'Articolo numero cinque',65);
Adesso compare un messaggio di errore:
SQL error: insert on table "ARTICOLO" violates foreign key constraint "fki_ARTICOLO_ID_RIVISTA_RIVISTA_ID"
Stessa cosa se proviamo a modificare l'id_rivista di qualche record già presente:
update ARTICOLO set id_rivista=3; SQL error: update on table "ARTICOLO" violates foreign key constraint "fku_ARTICOLO_ID_RIVISTA_RIVISTA_ID"
Se adesso cancelliamo una rivista, dovremmo cancellare a cascata anche gli articoli correlati:
delete from rivista where id=2;
Nessun errore. Vediamo adesso cosa è rimasto nella tabella degli articoli:
ID ID_RIVISTA TITOLO PAG -- ---------- ------------------- --- 1 1 Articolo numero uno 30 2 1 Articolo numero due 40
Bene abbiamo ovviato a questo fastidioso problemuccio, in attesa che questa regola venga fatta valere direttamente da Sqlite.
Autore
Questa guida è stata scritta da Christian Picone.

