356 votes

INSERT IF NOT EXISTS ELSE UPDATE ?

J'ai trouvé quelques solutions "possibles" pour le classique "Comment insérer un nouvel enregistrement ou en mettre un à jour s'il existe déjà", mais je n'arrive pas à les faire fonctionner en SQLite.

J'ai une table définie comme suit :

CREATE TABLE Book 
ID     INTEGER PRIMARY KEY AUTOINCREMENT,
Name   VARCHAR(60) UNIQUE,
TypeID INTEGER,
Level  INTEGER,
Seen   INTEGER

Ce que je veux faire, c'est ajouter un enregistrement avec un nom unique. Si le nom existe déjà, je veux modifier les champs.

Quelqu'un peut-il me dire comment faire, s'il vous plaît ?

7 votes

"insérer ou remplacer" est tout à fait différent de "insérer ou mettre à jour"

3 votes

Et si UPSERT ?

379voto

janm Points 9310

Jetez un coup d'œil à http://sqlite.org/lang_conflict.html .

Vous voulez quelque chose comme :

insert or replace into Book (ID, Name, TypeID, Level, Seen) values
((select ID from Book where Name = "SearchName"), "SearchName", ...);

Notez que tout champ ne figurant pas dans la liste d'insertion sera mis à NULL si la ligne existe déjà dans la table. C'est pourquoi il existe une sous-sélection pour le champ ID colonne : Dans le cas d'un remplacement, l'instruction lui attribue la valeur NULL, puis un nouvel ID est alloué.

Cette approche peut également être utilisée si vous souhaitez ne pas modifier la valeur d'un champ particulier dans la ligne dans le cas d'un remplacement, mais lui attribuer la valeur NULL dans le cas d'une insertion.

Par exemple, en supposant que vous voulez laisser Seen seul :

insert or replace into Book (ID, Name, TypeID, Level, Seen) values (
   (select ID from Book where Name = "SearchName"),
   "SearchName",
    5,
    6,
    (select Seen from Book where Name = "SearchName"));

135 votes

Faux "insérer ou remplacer" est différent de "insérer ou mettre à jour". Pour une réponse valable, voir stackoverflow.com/questions/418898/

17 votes

@rds Non, ce n'est pas faux car cette question dit "modifier les champs" et la clé primaire ne fait pas partie de la liste des colonnes, mais tous les autres champs le sont. Si vous avez des cas particuliers où vous ne remplacez pas toutes les valeurs des champs, ou si vous vous amusez avec la clé primaire, vous devriez faire quelque chose de différent. Si vous avez un ensemble complet de nouveaux champs, cette approche est parfaite. Avez-vous un problème spécifique que je ne vois pas ?

13 votes

C'est valable si vous connaissez toutes les nouvelles valeurs pour tous les champs. Si l'utilisateur ne met à jour que, disons, le Level cette approche ne peut être suivie.

126voto

moshik Points 81

Vous devez utiliser le INSERT OR IGNORE suivi d'une commande UPDATE commande : Dans l'exemple suivant name est une clé primaire :

INSERT OR IGNORE INTO my_table (name, age) VALUES ('Karen', 34)
UPDATE my_table SET age = 34 WHERE name='Karen'

La première commande va insérer l'enregistrement. Si l'enregistrement existe, elle ignorera l'erreur causée par le conflit avec une clé primaire existante.

La deuxième commande va mettre à jour l'enregistrement (qui existe bien maintenant)

9 votes

À quel moment il ignorera ? lorsque le nom et l'âge sont tous deux identiques ?

0 votes

Cela devrait être la solution... si vous utilisez un déclencheur sur l'insertion, la réponse acceptée se déclenche à chaque fois. Ceci ne le fait pas et n'effectue qu'une mise à jour

1 votes

Il ignore en se basant uniquement sur le nom. Rappelez-vous que seule la colonne "nom" est une clé primaire.

79voto

Gaspard Bucher Points 1283

Vous devez définir une contrainte sur la table pour déclencher une " conflit "que vous résolvez ensuite en effectuant un remplacement :

CREATE TABLE data   (id INTEGER PRIMARY KEY, event_id INTEGER, track_id INTEGER, value REAL);
CREATE UNIQUE INDEX data_idx ON data(event_id, track_id);

Alors vous pouvez émettre :

INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 2, 2, 3);
INSERT OR REPLACE INTO data VALUES (NULL, 1, 2, 5);

Le "SELECT * FROM data" vous donnera :

2|2|2|3.0
3|1|2|5.0

Notez que le data.id est "3" et non "1" car REPLACE effectue un DELETE et un INSERT, pas un UPDATE. Cela signifie également que vous devez vous assurer que vous définissez toutes les colonnes nécessaires, sinon vous obtiendrez des valeurs NULL inattendues.

51voto

Steely Wing Points 1857

INSERT OR REPLACE remplacera les autres champs par la valeur par défaut.

sqlite> CREATE TABLE Book (
  ID     INTEGER PRIMARY KEY AUTOINCREMENT,
  Name   TEXT,
  TypeID INTEGER,
  Level  INTEGER,
  Seen   INTEGER
);

sqlite> INSERT INTO Book VALUES (1001, 'C++', 10, 10, 0);

sqlite> SELECT * FROM Book;
1001|C++|10|10|0

sqlite> INSERT OR REPLACE INTO Book(ID, Name) VALUES(1001, 'SQLite');

sqlite> SELECT * FROM Book;
1001|SQLite|||

Si vous voulez conserver l'autre champ

  • Méthode 1

    sqlite> SELECT * FROM Book; 1001|C++|10|10|0

    sqlite> INSERT OR IGNORE INTO Book(ID) VALUES(1001); sqlite> UPDATE Book SET Name='SQLite' WHERE ID=1001;

    sqlite> SELECT * FROM Book; 1001|SQLite|10|10|0

  • Méthode 2

Utilisation de UPSERT (la syntaxe a été ajoutée à SQLite avec la version 3.24.0 (2018-06-04))

INSERT INTO Book (ID, Name)
  VALUES (1001, 'SQLite')
  ON CONFLICT (ID) DO
  UPDATE SET Name=excluded.Name;

Le site excluded. égal à la valeur dans VALUES ( 'SQLite' ).

44voto

Burçin Yazıcı Points 410

Tout d'abord, mettez-le à jour. Si nombre de lignes concernées \= 0 puis l'insérer. C'est le plus simple et le plus adapté à tous SGBDR .

12 votes

Deux opérations ne devraient pas poser de problème avec une transaction au bon niveau d'isolement, quelle que soit la base de données.

7 votes

Insert or Replace est vraiment plus préférable.

2 votes

J'aimerais vraiment que la documentation informatique contienne plus d'exemples. J'ai essayé ce qui suit et cela ne fonctionne pas (ma syntaxe est manifestement erronée). Avez-vous une idée de ce que cela devrait être ? INSERT INTO Book (Name,TypeID,Level,Seen) VALUES( 'Superman', '2', '14', '0' ) ON CONFLICT REPLACE Book (Name,TypeID,Level,Seen) VALUES( 'Superman', '2', '14', '0' )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X