Как мне обновить, если существует, вставить, если нет (AKA «upsert» или «объединить») в MySQL?

152

Есть ли простой способ для INSERTстроки, когда она не существует, или UPDATEесли она существует, используя один запрос MySQL?

реветь
источник

Ответы:

191

Использование INSERT ... ON DUPLICATE KEY UPDATE. Например:

INSERT INTO `usage`
(`thing_id`, `times_used`, `first_time_used`)
VALUES
(4815162342, 1, NOW())
ON DUPLICATE KEY UPDATE
`times_used` = `times_used` + 1
хаос
источник
10
Да, я считаю, что эквивалент SQL Server называется MERGE. В общем, понятие часто упоминается как "UPSERT".
хаос
3
@blub: если вы создадите уникальный ключ gebи topicон будет работать ( ALTER TABLE table ADD UNIQUE geb_by_topic (geb, topic)).
хаос
1
@Brian: также называется эквивалент Oracle, MERGEно я не уверен, что его синтаксис идентичен синтаксису SQL Server.
Кен Кинан
1
@Brooks: если вы передадите ему 0, он будет использовать 0 в качестве значения. Так что не делай этого. Не передавайте ничего, или передавайте a NULL, чтобы auto_incrementповедение работало (в противном случае, да, работает так, как вы предполагаете; см. Dev.mysql.com/doc/refman/5.5/en/example-auto-increment. HTML ).
хаос
3
Спасибо, и вы можете использовать, VALUES(col)чтобы получить значение из аргумента вставки в случае дублирования. Например:ON DUPLICATE UPDATE b = VALUES(b), c = VALUES(c)
Gerrytan
5

Я знаю, что это старый вопрос, но Google недавно привел меня сюда, поэтому я думаю, что другие тоже приходят сюда.

@chaos правильно: INSERT ... ON DUPLICATE KEY UPDATEсинтаксис есть.

Тем не менее, оригинальный вопрос задан именно о MySQL, и в MySQL есть REPLACE INTO ...синтаксис. ИМХО, эта команда проще и проще в использовании для upserts. Из руководства:

REPLACE работает точно так же, как INSERT, за исключением того, что если старая строка в таблице имеет то же значение, что и новая строка для PRIMARY KEY или индекса UNIQUE, старая строка удаляется перед вставкой новой строки.

Обратите внимание, что это не стандартный SQL. Пример из руководства:

CREATE TABLE test (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  data VARCHAR(64) DEFAULT NULL,
  ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
);

mysql> REPLACE INTO test VALUES (1, 'Old', '2014-08-20 18:47:00');
Query OK, 1 row affected (0.04 sec)

mysql> REPLACE INTO test VALUES (1, 'New', '2014-08-20 18:47:42');
Query OK, 2 rows affected (0.04 sec)

mysql> SELECT * FROM test;
+----+------+---------------------+
| id | data | ts                  |
+----+------+---------------------+
|  1 | New  | 2014-08-20 18:47:42 |
+----+------+---------------------+
1 row in set (0.00 sec)

Изменить: Просто справедливое предупреждение, REPLACE INTOкоторое не похоже UPDATE. Как сказано в руководстве, REPLACEудаляет строку, если она существует, а затем вставляет новую. (Обратите внимание на забавные «затронутые 2 строки» в приведенном выше примере.) То есть он заменит значения всех столбцов существующей записи (а не просто обновит некоторые столбцы.) Поведение MySQL во REPLACE INTOмногом похоже на поведение Sqlite INSERT OR REPLACE INTO. Посмотрите этот вопрос для некоторых обходных путей, если вы хотите обновить только несколько столбцов (и не все столбцы), если запись уже существует.

Броненосец джим
источник