web-dev-qa-db-fra.com

INSERT SI PAS EXISTE AUTRE MISE À JOUR?

J'ai trouvé quelques solutions "seraient" pour le classique "Comment insérer un nouvel enregistrement ou en mettre à jour un s'il existe déjà", mais je ne parviens pas à en faire utiliser un dans 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 souhaite modifier les champs.

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

252
SparkyNZ

Regardez 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'insertions sera défini sur NULL si la ligne existe déjà dans la table. C'est pourquoi il existe une sous-sélection pour la colonne ID: dans le cas d'un remplacement, l'instruction le définirait sur NULL et un nouvel ID serait alloué.

Cette approche peut également être utilisée si vous souhaitez laisser des valeurs de champ particulières seules si la ligne du cas de remplacement est définie, mais définissez le champ sur NULL dans le cas de l'insertion.

Par exemple, supposons que vous souhaitiez laisser Seen seule:

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"));
306
janm

Vous devez utiliser la commande INSERT OR IGNORE suivie d'une commande UPDATE: 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 insérera l'enregistrement. Si l'enregistrement existe, il ignorera l'erreur provoquée par le conflit avec une clé primaire existante.

La deuxième commande mettra à jour l'enregistrement (qui existe maintenant définitivement)

77
moshik

Vous devez définir une contrainte sur la table pour déclencher un " 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);

Ensuite, 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 INSERT, pas un UPDATE. Cela signifie également que vous devez vous assurer de définir toutes les colonnes nécessaires, sinon vous obtiendrez des valeurs NULL inattendues.

61
gaspard

Tout d'abord mettre à jour. Si nombre de lignes affectées = 0, insérez-le. C'est le plus facile et adapté à tous RDBMS.

32
Burçin

INSERT OR REPLACE remplacera les autres champs (TypeID, Level) à la valeur par défaut.

INSERT OR REPLACE INTO book(id, name) VALUES(1001, 'Programming')

J'utilise ceci

INSERT OR IGNORE INTO book(id) VALUES(1001);
UPDATE book SET name = 'Programming' WHERE id = 1001;

Vous pouvez aussi utiliser

INSERT OR REPLACE INTO book (id, name) 
VALUES (1001, 'Programming',
  (SELECT typeid FROM book WHERE id = 1001),
  (SELECT level FROM book WHERE id = 1001),
)

mais je pense que la première méthode est plus facile à lire

16
Steely Wing

psert c'est ce que vous voulez. La syntaxe UPSERT a été ajoutée à SQLite avec la version 3.24.0 (2018-06-04).

CREATE TABLE phonebook2(
  name TEXT PRIMARY KEY,
  phonenumber TEXT,
  validDate DATE
);

INSERT INTO phonebook2(name,phonenumber,validDate)
  VALUES('Alice','704-555-1212','2018-05-08')
  ON CONFLICT(name) DO UPDATE SET
    phonenumber=excluded.phonenumber,
    validDate=excluded.validDate
  WHERE excluded.validDate>phonebook2.validDate;

Soyez averti qu’à ce stade, le mot "UPSERT" ne fait pas partie de la syntaxe upsert.

La syntaxe correcte est

INSERT INTO ... ON CONFLICT(...) DO UPDATE SET...

et si vous utilisez INSERT INTO SELECT ... votre sélection doit au moins WHERE true résoudre l’ambiguïté de l’analyseur sur le jeton ON avec la syntaxe de jointure.

Soyez averti que INSERT OR REPLACE... supprimera l'enregistrement avant d'en insérer un nouveau s'il doit être remplacé, ce qui pourrait être mauvais si vous avez des cascades de clé étrangère ou d'autres déclencheurs de suppression.

7
ComradeJoecool

Si vous n'avez pas de clé primaire, vous pouvez insérer s'il n'existe pas, puis effectuez une mise à jour. La table doit contenir au moins une entrée avant de l'utiliser.

INSERT INTO Test 
   (id, name)
   SELECT 
      101 as id, 
      'Bob' as name
   FROM Test
       WHERE NOT EXISTS(SELECT * FROM Test WHERE id = 101 and name = 'Bob') LIMIT 1;

Update Test SET id='101' WHERE name='Bob';
5
matt

Je crois que tu veux UPSERT .

"INSERT OR REPLACE" sans la astuce supplémentaire dans cette réponse réinitialisera tous les champs non spécifiés sur NULL ou une autre valeur par défaut. (Ce comportement de INSERT OR REPLACE est différent de UPDATE; il est exactement identique à INSERT car il s'agit en fait d'INTER. Cependant, si vous vouliez être UPDATE-if-existe, vous voulez probablement la sémantique de UPDATE et vous serez désagréablement surpris par le résultat réel.)

La ruse de l'implémentation UPSERT suggérée consiste à utiliser INSERT OR REPLACE, mais à spécifier tous les champs, à l'aide de clauses SELECT intégrées pour récupérer la valeur actuelle des champs que vous ne souhaitez pas modifier.

3
metamatt

Je pense que cela vaut la peine de signaler qu'il peut y avoir un comportement inattendu ici si vous ne comprenez pas bien comment KEY PRIMARY et UNIQUE interagissent.

Par exemple, si vous souhaitez insérer un enregistrement uniquement si le champ NOM n'est pas utilisé actuellement, vous devez définir une exception de contrainte. vous, alors INSERT OR REPLACE ne lancera ni exception, mais résoudra la contrainte UNIQUE elle-même en remplaçant l'enregistrement en conflit (l'enregistrement existant avec le même NOM). Gaspard le démontre très bien dans sa réponse ci-dessus.

Si vous souhaitez qu'une exception de contrainte soit déclenchée, vous devez utiliser une instruction INSERT et vous appuyer sur une commande séparée UPDATE pour mettre à jour l'enregistrement une fois que vous connaissez la le nom n'est pas pris.

1
Matt Matthias