SQLite ha il ON CONFLICT
clausola che consente di specificare come gestire i conflitti di vincoli. Si applica a UNIQUE
, NOT NULL
, CHECK
e PRIMARY KEY
vincoli (ma non FOREIGN KEY
vincoli).
Ci sono cinque possibili opzioni che puoi usare con questa clausola:
ABORT
FAIL
IGNORE
REPLACE
ROLLBACK
Questo articolo fornisce esempi e una spiegazione di ciascuna di queste opzioni.
Il ON CONFLICT
La clausola è usata in CREATE TABLE
istruzioni, ma può essere utilizzato anche durante l'inserimento o l'aggiornamento di dati sostituendo ON CONFLICT
con OR
.
Durante la creazione della tabella
Come accennato, puoi usare ON CONFLICT
quando crei la tabella o quando inserisci/aggiorni i dati.
Ecco un esempio di utilizzo di ON CONFLICT
al momento della creazione della tabella.
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL ON CONFLICT IGNORE,
Price
);
Quando usi il ON CONFLICT
clausola, la applichi al vincolo specifico che desideri gestire. In questo caso, ho aggiunto la clausola a un NOT NULL
vincolo.
In questo caso ho specificato IGNORE
, il che significa che, in caso di violazione di un vincolo, SQLite salterà quella riga e quindi continuerà l'elaborazione.
Ora se provo a inserire NULL
nel NomeProdotto colonna quella riga viene saltata.
INSERT INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Quando si inseriscono i dati
Puoi anche utilizzare questa clausola durante l'inserimento e l'aggiornamento dei dati. La differenza è che sostituisci ON CONFLICT
con OR
.
Per dimostrare, eliminerò la tabella precedente e la creerò di nuovo, ma senza il ON CONFLICT
clausola:
DROP TABLE IF EXISTS Products;
CREATE TABLE Products(
ProductId INTEGER PRIMARY KEY,
ProductName NOT NULL,
Price
);
Ora inserirò gli stessi dati e userò OR IGNORE
per saltare la riga che viola il vincolo.
INSERT OR IGNORE INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Quindi otteniamo lo stesso risultato dell'esempio precedente.
In questi esempi ho usato IGNORE
opzione. Questa è solo una delle cinque possibili opzioni per questa clausola.
Di seguito sono riportati esempi che utilizzano ciascuna delle cinque opzioni.
Interrompi
Questa opzione interrompe l'istruzione SQL corrente con un errore SQLITE_CONSTRAINT e annulla tutte le modifiche apportate dall'istruzione SQL corrente; ma le modifiche causate da istruzioni SQL precedenti all'interno della stessa transazione vengono conservate e la transazione rimane attiva.
Questo è il comportamento predefinito. In altre parole, questo è ciò che accade durante le violazioni dei vincoli quando non si utilizza il ON CONFLICT
clausola.
Ecco un esempio di cosa succede quando specifichi ABORT
.
DELETE FROM Products;
INSERT OR ABORT INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Risultato:
Nessun risultato è stato restituito perché INSERT
operazione è stata interrotta e la tabella è quindi vuota.
Ecco cosa succede se inserisco ogni riga nel proprio INSERT
dichiarazione all'interno di una transazione.
BEGIN TRANSACTION;
INSERT OR ABORT INTO Products VALUES (1, 'Hammer', 9.99);
INSERT OR ABORT INTO Products VALUES (2, NULL, 1.49);
INSERT OR ABORT INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR ABORT INTO Products VALUES (4, 'Wrench', 37.00);
INSERT OR ABORT INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR ABORT INTO Products VALUES (6, 'Bandage', 120.00);
COMMIT;
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Fallito
Il FAIL
l'opzione interrompe l'istruzione SQL corrente con un errore SQLITE_CONSTRAINT. Ma non annulla le modifiche precedenti dell'istruzione SQL non riuscite né termina la transazione.
Ecco un esempio.
DELETE FROM Products;
INSERT OR FAIL INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99
Eccolo con INSERT
separato dichiarazioni all'interno di una transazione.
DELETE FROM Products;
BEGIN TRANSACTION;
INSERT OR FAIL INTO Products VALUES (1, 'Hammer', 9.99);
INSERT OR FAIL INTO Products VALUES (2, NULL, 1.49);
INSERT OR FAIL INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR FAIL INTO Products VALUES (4, 'Wrench', 37.00);
INSERT OR FAIL INTO Products VALUES (5, 'Chisel', 23.00);
INSERT OR FAIL INTO Products VALUES (6, 'Bandage', 120.00);
COMMIT;
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Ignora
Il IGNORE
l'opzione salta l'unica riga che contiene la violazione del vincolo e continua a elaborare le righe successive dell'istruzione SQL come se nulla fosse andato storto. Altre righe prima e dopo la riga che conteneva la violazione del vincolo vengono inserite o aggiornate normalmente. Non viene restituito alcun errore per l'unicità, NOT NULL
e UNIQUE
errori di vincolo quando viene utilizzata questa opzione. Tuttavia, questa opzione funziona come ABORT
per errori di vincolo di chiave esterna.
I primi esempi in questa pagina usano IGNORE
, ma eccolo di nuovo qui.
DELETE FROM Products;
INSERT OR IGNORE INTO Products VALUES
(1, 'Hammer', 9.99),
(2, NULL, 1.49),
(3, 'Saw', 11.34),
(4, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Hammer 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Sostituisci
Il REPLACE
l'opzione funziona in modo diverso a seconda della violazione:
- Quando un
UNIQUE
oPRIMARY KEY
si verifica una violazione del vincolo, ilREPLACE
l'opzione elimina le righe preesistenti che causano la violazione del vincolo prima di inserire o aggiornare la riga corrente e il comando continua a essere eseguito normalmente. - Se un
NOT NULL
si verifica una violazione del vincolo, sostituisce ilNULL
valore con il valore predefinito per quella colonna, o se la colonna non ha un valore predefinito, alloraABORT
viene utilizzato l'algoritmo. - Se un
CHECK
si verifica una violazione del vincolo o del vincolo della chiave esterna, quindiREPLACE
funziona comeABORT
.
Inoltre, se elimina le righe per soddisfare un vincolo, i trigger di eliminazione si attivano se e solo se i trigger ricorsivi sono abilitati.
Ecco un esempio che utilizza REPLACE
opzione.
DELETE FROM Products;
INSERT OR REPLACE INTO Products VALUES
(1, 'Hammer', 9.99),
(2, 'Nails', 1.49),
(3, 'Saw', 11.34),
(1, 'Wrench', 37.00),
(5, 'Chisel', 23.00),
(6, 'Bandage', 120.00);
SELECT * FROM Products;
Risultato:
ProductId ProductName Price ---------- ----------- ---------- 1 Wrench 37.0 2 Nails 1.49 3 Saw 11.34 5 Chisel 23.0 6 Bandage 120.0
In questo esempio, il conflitto era con la chiave primaria (ho provato a inserire due righe con lo stesso ProductId ). Il REPLACE
opzione ha fatto sì che il secondo sostituisse il primo.
Ripristino
Un'altra opzione è usare ROLLBACK
.
Questa opzione 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.
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', 9.99);
INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49);
INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00);
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> DELETE FROM Products; sqlite> sqlite> BEGIN TRANSACTION; sqlite> INSERT OR ROLLBACK INTO Products VALUES (1, 'Hammer', 9.99); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49); Error: NOT NULL constraint failed: Products.ProductName sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34); sqlite> INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00); 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 ---------- ----------- ---------- 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
Quindi è arrivato alla violazione del vincolo, quindi ha annullato la transazione. Quindi sono state elaborate le 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', 9.99);
INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49);
INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00);
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', 9.99); sqlite> INSERT OR ROLLBACK INTO Products VALUES (2, NULL, 1.49); Error: NOT NULL constraint failed: Products.ProductName sqlite> INSERT OR ROLLBACK INTO Products VALUES (3, 'Saw', 11.34); sqlite> INSERT OR ROLLBACK INTO Products VALUES (4, 'Wrench', 37.00); 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 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0
In questo caso, ha funzionato come ABORT
.
Per confermare, ecco la stessa dichiarazione utilizzando ABORT
invece di ROLLBACK
.
DELETE FROM Products;
INSERT OR ABORT INTO Products VALUES (1, 'Hammer', 9.99);
INSERT OR ABORT INTO Products VALUES (2, NULL, 1.49);
INSERT OR ABORT INTO Products VALUES (3, 'Saw', 11.34);
INSERT OR ABORT INTO Products VALUES (4, 'Wrench', 37.00);
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', 9.99); sqlite> INSERT OR ABORT INTO Products VALUES (2, NULL, 1.49); Error: NOT NULL constraint failed: Products.ProductName sqlite> INSERT OR ABORT INTO Products VALUES (3, 'Saw', 11.34); sqlite> INSERT OR ABORT INTO Products VALUES (4, 'Wrench', 37.00); 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 9.99 3 Saw 11.34 4 Wrench 37.0 5 Chisel 23.0 6 Bandage 120.0