Bagaimana saya memperbarui jika ada, masukkan jika tidak (AKA "upsert" atau "merge") di MySQL?

152

Apakah ada cara mudah untuk INSERTbaris ketika tidak ada, atau UPDATEjika ada, menggunakan satu permintaan MySQL?

blub
sumber

Jawaban:

191

Gunakan INSERT ... ON DUPLICATE KEY UPDATE. Sebagai contoh:

INSERT INTO `usage`
(`thing_id`, `times_used`, `first_time_used`)
VALUES
(4815162342, 1, NOW())
ON DUPLICATE KEY UPDATE
`times_used` = `times_used` + 1
kekacauan
sumber
10
Ya, saya percaya setara SQL Server disebut MERGE. Secara umum, konsep ini sering disebut sebagai "UPSERT".
kekacauan
3
@blub: Jika Anda membuat kunci unik gebdan topicitu akan berfungsi ( ALTER TABLE table ADD UNIQUE geb_by_topic (geb, topic)).
kekacauan
1
@ Brian: Setara dengan Oracle juga disebut MERGEtapi saya tidak yakin apakah sintaksnya identik dengan SQL Server.
Ken Keenan
1
@ Brooks: Jika Anda memberikannya 0, itu benar-benar akan menggunakan 0 sebagai nilainya. Jadi jangan lakukan itu. Jangan berikan apa pun, atau berikan a NULL, untuk membiarkan auto_incrementperilaku berfungsi (yang sebaliknya, ya, berfungsi seperti yang Anda duga ; lihat dev.mysql.com/doc/refman/5.5/en/example-auto-increment. html ).
kekacauan
3
Terima kasih, dan Anda dapat menggunakan VALUES(col)untuk mendapatkan nilai dari argumen insert jika ada duplikat. Contoh:ON DUPLICATE UPDATE b = VALUES(b), c = VALUES(c)
gerrytan
5

Saya tahu ini adalah pertanyaan lama, tetapi Google membawa saya ke sini baru-baru ini, jadi saya membayangkan orang lain juga datang ke sini.

@chaos benar: ada INSERT ... ON DUPLICATE KEY UPDATEsintaksnya.

Namun, pertanyaan awal ditanyakan tentang MySQL secara khusus, dan di MySQL ada REPLACE INTO ...sintaksisnya. IMHO, perintah ini lebih mudah dan lebih mudah digunakan untuk upert. Dari manual:

REPLACE bekerja persis seperti INSERT, kecuali bahwa jika baris lama dalam tabel memiliki nilai yang sama dengan baris baru untuk KUNCI UTAMA atau indeks UNIK, baris lama dihapus sebelum baris baru dimasukkan.

Perhatikan ini bukan SQL standar. Contoh dari manual:

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)

Sunting: Hanya peringatan wajar yang REPLACE INTOtidak disukai UPDATE. Seperti yang dikatakan manual, REPLACEhapus baris jika ada, lalu sisipkan yang baru. (Catat lucu "2 baris yang terpengaruh" dalam contoh di atas.) Artinya, itu akan menggantikan nilai semua kolom dari catatan yang ada (dan tidak hanya memperbarui beberapa kolom.) Perilaku MySQL REPLACE INTOmirip dengan Sqlite INSERT OR REPLACE INTO. Lihat pertanyaan ini untuk beberapa solusi jika Anda hanya ingin memperbarui beberapa kolom (dan tidak semua kolom) jika catatan sudah ada.

Armadillo Jim
sumber