SQLite
 sql >> Database >  >> RDS >> SQLite

Come funziona ON CONFLICT in SQLite

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 o PRIMARY KEY si verifica una violazione del vincolo, il REPLACE 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 il NULL valore con il valore predefinito per quella colonna, o se la colonna non ha un valore predefinito, allora ABORT viene utilizzato l'algoritmo.
  • Se un CHECK si verifica una violazione del vincolo o del vincolo della chiave esterna, quindi REPLACE funziona come ABORT .

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