Wie aktualisiere ich, falls vorhanden, füge es in MySQL ein, wenn nicht (AKA "upsert" oder "merge")?

152

Gibt es eine einfache Möglichkeit, INSERTeine Zeile zu verwenden, wenn sie nicht vorhanden ist, oder UPDATEmithilfe einer MySQL-Abfrage , wenn sie vorhanden ist?

blub
quelle

Antworten:

191

Verwenden Sie INSERT ... ON DUPLICATE KEY UPDATE. Beispielsweise:

INSERT INTO `usage`
(`thing_id`, `times_used`, `first_time_used`)
VALUES
(4815162342, 1, NOW())
ON DUPLICATE KEY UPDATE
`times_used` = `times_used` + 1
Chaos
quelle
10
Ja, ich glaube, das Äquivalent von SQL Server heißt MERGE. Im Allgemeinen wird das Konzept oft als bezeichnet "UPSERT".
Chaos
3
@blub: Wenn Sie einen eindeutigen Schlüssel erstellen gebund topicdieser funktioniert ( ALTER TABLE table ADD UNIQUE geb_by_topic (geb, topic)).
Chaos
1
@Brian: Das Äquivalent von Oracle wird auch genannt, MERGEaber ich bin nicht sicher, ob seine Syntax mit der von SQL Server identisch ist.
Ken Keenan
1
@Brooks: Wenn Sie eine 0 übergeben, wird tatsächlich 0 als Wert verwendet. Also tu das nicht. Übergeben Sie nichts oder kein a NULL, damit das auto_incrementVerhalten funktioniert (was ansonsten ja so funktioniert, wie Sie es annehmen ; siehe dev.mysql.com/doc/refman/5.5/en/example-auto-increment). html ).
Chaos
3
Vielen Dank, und Sie können verwenden VALUES(col), um den Wert aus dem Einfüge-Argument im Falle eines Duplikats zu erhalten. ZB:ON DUPLICATE UPDATE b = VALUES(b), c = VALUES(c)
Gerrytan
5

Ich weiß, dass dies eine alte Frage ist, aber Google hat mich kürzlich hierher geführt, sodass ich mir vorstellen kann, dass auch andere hierher kommen.

@chaos ist richtig: Es gibt die INSERT ... ON DUPLICATE KEY UPDATESyntax.

Die ursprüngliche Frage wurde jedoch speziell zu MySQL gestellt, und in MySQL gibt es die REPLACE INTO ...Syntax. IMHO ist dieser Befehl einfacher und unkomplizierter für Upserts zu verwenden. Aus dem Handbuch:

REPLACE funktioniert genau wie INSERT, außer dass, wenn eine alte Zeile in der Tabelle denselben Wert wie eine neue Zeile für einen PRIMARY KEY oder einen UNIQUE-Index hat, die alte Zeile gelöscht wird, bevor die neue Zeile eingefügt wird.

Beachten Sie, dass dies kein Standard-SQL ist. Ein Beispiel aus dem Handbuch:

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)

Edit: Nur eine faire Warnung, REPLACE INTOdie nicht so ist UPDATE. Wie im Handbuch angegeben, wird REPLACEdie vorhandene Zeile gelöscht und eine neue eingefügt. (Beachten Sie die lustigen "2 betroffenen Zeilen" im obigen Beispiel.) Das heißt, es ersetzt die Werte aller Spalten eines vorhandenen Datensatzes (und aktualisiert nicht nur einige Spalten.) Das Verhalten von MySQL REPLACE INTOist dem von Sqlite sehr ähnlich INSERT OR REPLACE INTO. In dieser Frage finden Sie einige Problemumgehungen, wenn Sie nur einige Spalten (und nicht alle Spalten) aktualisieren möchten, wenn der Datensatz bereits vorhanden ist.

Gürteltier Jim
quelle