SQLite ha una clausola di estensione SQL non standard chiamata ON CONFLICT
che ci consente di specificare come gestire i conflitti di vincoli.
In particolare, la clausola si applica a UNIQUE
, NOT NULL
, CHECK
e PRIMARY KEY
vincoli.
Questo articolo fornisce esempi di come questa clausola può essere utilizzata per determinare come gestire i conflitti di vincoli di chiave primaria.
Per "conflitti di vincolo della chiave primaria", intendo quando si tenta di inserire un valore duplicato in una colonna della chiave primaria. Per impostazione predefinita, quando si tenta di eseguire questa operazione, l'operazione verrà interrotta e SQLite restituirà un errore.
Ma puoi usare il ON CONFLICT
clausola per cambiare il modo in cui SQLite affronta queste situazioni.
Un'opzione consiste nell'utilizzare questa clausola in CREATE TABLE
dichiarazione durante la creazione della tabella. Ciò determinerà come tutti INSERT
le operazioni vengono trattate.
Un'altra opzione è usare la clausola su INSERT
istruzione ogni volta che si tenta di inserire dati nella tabella. Questo ti permette di sfruttare la clausola anche quando la tabella non è stata creata con essa. Quando si utilizza questa opzione, la sintassi è diversa; usi OR
invece di ON CONFLICT
.
Gli esempi in questa pagina utilizzano la seconda opzione:creo la tabella senza il ON CONFLICT
clausola, e invece specifico OR
sul INSERT
dichiarazione.
Tabella di esempio
Creiamo una tabella semplice e aggiungiamo una riga.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName,
Price
);
INSERT INTO Products VALUES (1, 'Hammer', 8.00);
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Al momento abbiamo una riga, con un ProductId di 1 .
Ora possiamo esaminare i vari scenari di inserimento di dati in quella tabella che viola il vincolo della chiave primaria.
Esempio 1 – Interrompi (comportamento predefinito)
Come accennato, il comportamento predefinito per SQLite è interrompere INSERT
operazione e restituisce un errore.
INSERT INTO Products VALUES (1, 'Wrench', 12.50);
Risultato:
Error: UNIQUE constraint failed: Products.ProductId
È stato restituito un errore e non è stato inserito nulla.
Questo equivale a usare OR ABORT
opzione.
INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 12.50);
Risultato:
Error: UNIQUE constraint failed: Products.ProductId
Possiamo verificare che non sia stato inserito nulla eseguendo un SELECT
dichiarazione contro il tavolo.
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0
Possiamo vedere che la tabella contiene solo la riga originale.
Esempio 2 – Ignora
Un'alternativa è fare in modo che SQLite ignori la riga incriminata. In altre parole, salterà la riga e continuerà a elaborare le righe successive.
Per farlo all'interno del tuo INSERT
istruzione, usa OR IGNORE
.
L'effetto di ciò è che il INSERT
l'operazione riesce, ma senza righe che violano il vincolo della chiave primaria.
INSERT OR IGNORE INTO Products VALUES
(1, 'Hammer', 12.00),
(2, 'Nails', 2.50),
(3, 'Saw', 10.50),
(1, 'Wrench', 22.50),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
In questo caso ho provato a inserire due nuove righe con un ID che esisteva già nella tabella, quindi entrambe le righe sono state saltate.
Esempio 3 – Sostituisci
Un'altra opzione che hai è sostituire la riga originale con la nuova riga.
In altre parole, sovrascriverai i dati esistenti con quelli nuovi.
Per fare ciò, usa OR REPLACE
.
INSERT OR REPLACE INTO Products VALUES
(1, 'Hammer', 12.00),
(2, 'Nails', 2.50),
(3, 'Saw', 10.50),
(1, 'Wrench', 22.50),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Wrench 22.5 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0
In questo caso la maggior parte delle righe erano le stesse, quindi contengono gli stessi dati dopo INSERT
operazione. Tuttavia, possiamo vedere che la prima riga è stata aggiornata per utilizzare i valori nel mio INSERT
dichiarazione.
Possiamo anche vedere che utilizzava il secondo set di valori (visto che due condividevano lo stesso ProductId ).
Quindi l'effetto è un po' come un UPDATE
istruzione e INSERT
dichiarazione combinata.
Esempio 4:ripristino
Un'altra opzione è usare il ROLLBACK
opzione.
Ciò interrompe l'istruzione SQL corrente con un errore SQLITE_CONSTRAINT e ripristina la transazione corrente. Se nessuna transazione è attiva (a parte la transazione implicita che viene creata su ogni comando), funziona allo stesso modo di ABORT
algoritmo.
Vale la pena essere consapevoli di come funziona questa opzione. Ecco un esempio che utilizza più INSERT OR ROLLBACK
dichiarazioni all'interno di una transazione.
DELETE FROM Products;
BEGIN TRANSACTION;
INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 8.00);
INSERT OR ROLLBACK INTO Products VALUES (2, 'Nails', 2.50);
INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 10.50);
INSERT OR ROLLBACK INTO Products VALUES (1, 'Wrench', 22.50);
INSERT OR ROLLBACK INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR ROLLBACK INTO Products VALUES (6, 'Bandage', 120.00);
COMMIT;
SELECT * FROM Products;
Ecco l'output completo dal mio terminale quando eseguo questo:
sqlite> BEGIN TRANSACTION; sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 8.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, 'Nails', 2.50); sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 10.50); sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Wrench', 22.50); Error: UNIQUE constraint failed: Products.ProductId sqlite> INSERT OR ROLLBACK INTO Products VALUES (5, 'Chisel', 23.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (6, 'Bandage', 120.00); sqlite> COMMIT; Error: cannot commit - no transaction is active sqlite> sqlite> SELECT * FROM Products; ProductId ProductName Price ---------- ----------- ---------- 5 Chisel 23.0 6 Bandage 120.0 sqlite>
Fondamentalmente quello che è successo qui è che, è arrivato fino alla violazione del vincolo, quindi ha annullato la transazione. Quindi sono state elaborate le due righe successive e quindi il COMMIT
è stata rilevata la parola chiave. A quel punto, la transazione era già stata annullata e quindi abbiamo ricevuto un altro errore che ci informava che nessuna transazione era attiva.
Ecco cosa succede se lo rimuovo dalla transazione.
DELETE FROM Products;
INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 8.00);
INSERT OR ROLLBACK INTO Products VALUES (2, 'Nails', 2.50);
INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 10.50);
INSERT OR ROLLBACK INTO Products VALUES (1, 'Wrench', 22.50);
INSERT OR ROLLBACK INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR ROLLBACK INTO Products VALUES (6, 'Bandage', 120.00);
SELECT * FROM Products;
Ecco l'output completo dal mio terminale quando eseguo questo:
sqlite> DELETE FROM Products; sqlite> sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 8.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, 'Nails', 2.50); sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 10.50); sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Wrench', 22.50); Error: UNIQUE constraint failed: Products.ProductId sqlite> INSERT OR ROLLBACK INTO Products VALUES (5, 'Chisel', 23.00); sqlite> INSERT OR ROLLBACK INTO Products VALUES (6, 'Bandage', 120.00); sqlite> sqlite> SELECT * FROM Products; ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0 sqlite>
In questo caso, ha funzionato come ABORT
.
Per dimostrarlo, ecco la stessa affermazione usando ABORT
invece di ROLLBACK
.
DELETE FROM Products;
INSERT OR ABORT INTO Products VALUES (1, 'Hammer', 8.00);
INSERT OR ABORT INTO Products VALUES (2, 'Nails', 2.50);
INSERT OR ABORT INTO Products VALUES (3, 'Saw', 10.50);
INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 22.50);
INSERT OR ABORT INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR ABORT INTO Products VALUES (6, 'Bandage', 120.00);
SELECT * FROM Products;
Ecco l'output completo dal mio terminale quando eseguo questo:
sqlite> DELETE FROM Products; sqlite> sqlite> INSERT OR ABORT INTO Products VALUES (1, 'Hammer', 8.00); sqlite> INSERT OR ABORT INTO Products VALUES (2, 'Nails', 2.50); sqlite> INSERT OR ABORT INTO Products VALUES (3, 'Saw', 10.50); sqlite> INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 22.50); Error: UNIQUE constraint failed: Products.ProductId sqlite> INSERT OR ABORT INTO Products VALUES (5, 'Chisel', 23.00); sqlite> INSERT OR ABORT INTO Products VALUES (6, 'Bandage', 120.00); sqlite> sqlite> SELECT * FROM Products; ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5 5 Chisel 23.0 6 Bandage 120.0 sqlite>
L'opzione fallita
Il FAIL
l'opzione interrompe l'istruzione SQL corrente con un errore SQLITE_CONSTRAINT. Ma questa opzione non annulla le modifiche precedenti dell'istruzione SQL non riuscite né termina la transazione.
DELETE FROM Products;
INSERT OR FAIL INTO Products VALUES
(1, 'Hammer', 8.00),
(2, 'Nails', 2.50),
(3, 'Saw', 10.50),
(1, 'Wrench', 22.50),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 8.0 2 Nails 2.5 3 Saw 10.5