SQLite
 sql >> Base de données >  >> RDS >> SQLite

Gestion des conflits de clé primaire lors de l'insertion de données dans SQLite

SQLite a une clause d'extension SQL non standard appelée ON CONFLICT qui nous permet de spécifier comment traiter les conflits de contraintes.

En particulier, la clause s'applique à UNIQUE , NOT NULL , CHECK , et PRIMARY KEY contraintes.

Cet article fournit des exemples d'utilisation de cette clause pour déterminer comment gérer les conflits de contraintes de clé primaire.

Par "conflits de contrainte de clé primaire", je veux dire lorsque vous essayez d'insérer une valeur en double dans une colonne de clé primaire. Par défaut, lorsque vous essayez de le faire, l'opération sera abandonnée et SQLite renverra une erreur.

Mais vous pouvez utiliser le ON CONFLICT clause pour changer la façon dont SQLite traite ces situations.

Une option consiste à utiliser cette clause dans le CREATE TABLE déclaration lors de la création de la table. Cela déterminera comment tous les INSERT les opérations sont traitées.

Une autre option consiste à utiliser la clause sur le INSERT chaque fois que vous essayez d'insérer des données dans la table. Cela vous permet de profiter de la clause même lorsque la table n'a pas été créée avec elle. Lorsque vous utilisez cette option, la syntaxe est différente; vous utilisez OR au lieu de ON CONFLICT .

Les exemples sur cette page utilisent la deuxième option - je crée la table sans le ON CONFLICT clause, et je spécifie à la place OR sur le INSERT déclaration.

Exemple de tableau

Créons un tableau simple et ajoutons une ligne.

CREATE TABLE Products( 
    ProductId INTEGER PRIMARY KEY, 
    ProductName, 
    Price
);

INSERT INTO Products VALUES (1, 'Hammer', 8.00);

SELECT * FROM Products;

Résultat :

ProductId   ProductName  Price     
----------  -----------  ----------
1           Hammer       8.0       

Nous avons actuellement une ligne, avec un ProductId sur 1 .

Nous pouvons maintenant parcourir les différents scénarios d'insertion de données dans cette table qui violent la contrainte de clé primaire.

Exemple 1 - Abandonner (comportement par défaut)

Comme mentionné, le comportement par défaut pour SQLite est d'abandonner le INSERT opération et renvoie une erreur.

INSERT INTO Products VALUES (1, 'Wrench', 12.50);

Résultat :

Error: UNIQUE constraint failed: Products.ProductId

Une erreur a été renvoyée et rien n'a été inséré.

C'est l'équivalent d'utiliser le OR ABORT option.

INSERT OR ABORT INTO Products VALUES (1, 'Wrench', 12.50);

Résultat :

Error: UNIQUE constraint failed: Products.ProductId

Nous pouvons vérifier que rien n'a été inséré en exécutant un SELECT déclaration contre la table.

SELECT * FROM Products;

Résultat :

ProductId   ProductName  Price     
----------  -----------  ----------
1           Hammer       8.0       

Nous pouvons voir que le tableau ne contient que la ligne d'origine.

Exemple 2 – Ignorer

Une alternative consiste à faire en sorte que SQLite ignore la ligne incriminée. En d'autres termes, il ignorera la ligne et continuera à traiter les lignes suivantes.

Pour ce faire dans votre INSERT instruction, utilisez OR IGNORE .

L'effet de ceci est que le INSERT l'opération réussit, mais sans aucune ligne qui viole la contrainte de clé primaire.

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;

Résultat :

ProductId   ProductName  Price     
----------  -----------  ----------
1           Hammer       8.0       
2           Nails        2.5       
3           Saw          10.5      
5           Chisel       23.0      
6           Bandage      120.0     

Dans ce cas, j'ai essayé d'insérer deux nouvelles lignes avec un ID qui existait déjà dans le tableau, donc ces deux lignes ont été ignorées.

Exemple 3 – Remplacer

Une autre option consiste à remplacer la ligne d'origine par la nouvelle ligne.

En d'autres termes, vous écraserez les données existantes avec vos nouvelles données.

Pour ce faire, utilisez 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;

Résultat :

ProductId   ProductName  Price     
----------  -----------  ----------
1           Wrench       22.5      
2           Nails        2.5       
3           Saw          10.5      
5           Chisel       23.0      
6           Bandage      120.0     

Dans ce cas, la plupart des lignes étaient identiques, elles contiennent donc les mêmes données après le INSERT opération. Cependant, nous pouvons voir que la première ligne a été mise à jour pour utiliser les valeurs de mon INSERT déclaration.

Nous pouvons également voir qu'il a utilisé le deuxième ensemble de valeurs (vu que deux partageaient le même ProductId ).

Donc, l'effet est un peu comme une UPDATE déclaration et INSERT déclaration combinée.

Exemple 4 – Restauration

Une autre option consiste à utiliser le ROLLBACK option.

Cela interrompt l'instruction SQL en cours avec une erreur SQLITE_CONSTRAINT et annule la transaction en cours. Si aucune transaction n'est active (autre que la transaction implicite créée sur chaque commande), cela fonctionne de la même manière que ABORT algorithme.

Il vaut mieux être conscient du fonctionnement de cette option. Voici un exemple qui utilise plusieurs INSERT OR ROLLBACK relevés dans une transaction.

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;

Voici la sortie complète de mon terminal lorsque j'exécute ceci :

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> 

Fondamentalement, ce qui s'est passé ici, c'est qu'il est allé jusqu'à la violation de contrainte, puis a annulé la transaction. Ensuite, les deux lignes suivantes ont été traitées, puis le COMMIT mot clé a été rencontré. À ce moment-là, la transaction avait déjà été annulée et nous avons donc reçu une autre erreur nous indiquant qu'aucune transaction n'était active.

Voici ce qui se passe si je le supprime de la transaction.

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;

Voici la sortie complète de mon terminal lorsque j'exécute ceci :

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>

Dans ce cas, cela a fonctionné comme ABORT .

Pour le démontrer, voici la même instruction utilisant ABORT au lieu de 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;

Voici la sortie complète de mon terminal lorsque j'exécute ceci :

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'option d'échec

Le FAIL L'option abandonne l'instruction SQL en cours avec une erreur SQLITE_CONSTRAINT. Mais cette option n'annule pas les modifications précédentes de l'instruction SQL qui a échoué et ne met pas fin à la transaction.

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;

Résultat :

ProductId   ProductName  Price     
----------  -----------  ----------
1           Hammer       8.0       
2           Nails        2.5       
3           Saw          10.5