SQLite supporta un sottoinsieme molto limitato di ALTER TABLE
dichiarazione. Le uniche cose che puoi fare con ALTER TABLE
in SQLite è rinominare una tabella, rinominare una colonna all'interno di una tabella o aggiungere una nuova colonna a una tabella esistente.
In altre parole, non puoi usare ALTER TABLE
per aggiungere una chiave esterna a una tabella esistente come puoi fare in altri sistemi di gestione di database.
Pertanto, l'unico modo per "aggiungere" una chiave esterna a una tabella esistente in SQLite è creare una nuova tabella con una chiave esterna, quindi trasferire i dati nella nuova tabella.
C'è più di un modo per farlo, ma c'è un modo consigliato.
Il modo consigliato
La documentazione di SQLite consiglia un processo in 12 fasi per apportare modifiche allo schema a una tabella.
Ai fini di questo articolo, ci occuperemo solo dell'aggiunta di una chiave esterna.
Per renderlo un po' realistico, ci assicureremo che la tabella contenga già dei dati.
Tabella originale senza chiave esterna
Per prima cosa, creiamo una tabella senza una chiave esterna e popolarla con i dati.
CREATE TABLE Types(
TypeId INTEGER PRIMARY KEY,
Type
);
CREATE TABLE Pets(
PetId INTEGER PRIMARY KEY,
PetName,
TypeId
);
INSERT INTO Types VALUES
( NULL, 'Dog' ),
( NULL, 'Cat' ),
( NULL, 'Parakeet' ),
( NULL, 'Hamster' );
INSERT INTO Pets VALUES
( NULL, 'Brush', 3 ),
( NULL, 'Tweet', 3 ),
( NULL, 'Yelp', 1 ),
( NULL, 'Woofer', 1 ),
( NULL, 'Fluff', 2 );
In realtà, qui ho creato due tabelle e le ho popolate con i dati. Due tabelle, perché una (Tipi ) avrà la chiave primaria e l'altra (Animali ) avrà la chiave esterna.
Nota che non ho creato una chiave esterna.
Possiamo verificare che non ci siano chiavi esterne eseguendo il seguente comando:
PRAGMA foreign_key_list(Pets);
Nel mio caso, ottengo il seguente risultato:
(Questo è vuoto perché non ci sono vincoli di chiave esterna su questa tabella.)
Ora "aggiungiamo" una chiave esterna.
Aggiungi chiave esterna
Il codice seguente aggiunge una chiave esterna alla nostra tabella creando una nuova tabella con un vincolo di chiave esterna, trasferendo i dati a quella tabella, rilasciando la tabella originale, quindi rinominando la nuova tabella con il nome della tabella originale.
PRAGMA foreign_keys = OFF;
BEGIN TRANSACTION;
CREATE TABLE Pets_new(
PetId INTEGER PRIMARY KEY,
PetName,
TypeId,
FOREIGN KEY(TypeId) REFERENCES Types(TypeId)
);
INSERT INTO Pets_new SELECT * FROM Pets;
DROP TABLE Pets;
ALTER TABLE Pets_new RENAME TO Pets;
COMMIT;
PRAGMA foreign_keys = ON;
Fatto.
Se hai bisogno di ricostruire indici, trigger o viste, fallo dopo ALTER TABLE
istruzione che rinomina la tabella (appena prima di COMMIT
).
Ora controlliamo di nuovo la tabella per i vincoli di chiave esterna.
.mode line
PRAGMA foreign_key_list(Pets);
Risultato (usando l'output verticale):
id = 0 seq = 0 table = Types from = TypeId to = TypeId on_update = NO ACTION on_delete = NO ACTION match = NONE
Questa volta possiamo vedere i dettagli del vincolo di chiave esterna.
Nota che la prima riga del mio comando (.mode line
) non ha nulla a che fare con la creazione di una chiave esterna. L'ho messo lì esclusivamente per cambiare il modo in cui il mio terminale emette il risultato (in modo da non dover scorrere lateralmente per visualizzare il risultato).
Un metodo alternativo
Osservando l'esempio precedente, potresti pensare che esiste un modo più efficiente per farlo. Ad esempio potresti farlo in questo modo:
PRAGMA foreign_keys = OFF;
BEGIN TRANSACTION;
ALTER TABLE Pets RENAME TO Pets_old;
CREATE TABLE Pets(
PetId INTEGER PRIMARY KEY,
PetName,
TypeId,
FOREIGN KEY(TypeId) REFERENCES Types(TypeId)
);
INSERT INTO Pets SELECT * FROM Pets_old;
DROP TABLE Pets_old;
COMMIT;
PRAGMA foreign_keys = ON;
Ed è vero. Con il mio esempio, questo metodo funziona altrettanto bene.
Ma questo metodo ha anche il potenziale per corrompere i riferimenti alla tabella in qualsiasi trigger, vista e vincolo di chiave esterna esistenti.
Quindi, se la tua tabella ha già trigger, viste o vincoli di chiave esterna esistenti, è probabilmente più sicuro utilizzare il metodo consigliato.