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

Gestione dei conflitti di chiavi primarie durante l'inserimento di dati in SQLite

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