158

Is there an easy way to INSERT a row when it does not exist, or to UPDATE if it exists, using one MySQL query?

2 Answers 2

200

Use INSERT ... ON DUPLICATE KEY UPDATE. For example:

INSERT INTO `usage`
(`thing_id`, `times_used`, `first_time_used`)
VALUES
(4815162342, 1, NOW())
ON DUPLICATE KEY UPDATE
`times_used` = `times_used` + 1
18
  • 11
    Yeah, I believe SQL Server's equivalent is called MERGE. In general, the concept is often referred to as "UPSERT".
    – chaos
    Commented Aug 2, 2009 at 13:40
  • 3
    @blub: If you create a unique key on geb and topic it will work (ALTER TABLE table ADD UNIQUE geb_by_topic (geb, topic)).
    – chaos
    Commented Aug 2, 2009 at 13:43
  • 1
    @Brian: Oracle's equivalent is also called MERGE but I'm not sure if its syntax is identical to SQL Server's.
    – Ken Keenan
    Commented Aug 2, 2009 at 14:20
  • 1
    @Brooks: If you pass it a 0, it will actually use 0 as the value. So don't do that. Don't pass it anything, or pass it a NULL, to allow the auto_increment behavior to work (which otherwise, yes, works as you presume; see dev.mysql.com/doc/refman/5.5/en/example-auto-increment.html).
    – chaos
    Commented May 17, 2012 at 16:56
  • 3
    Thank you, and you can use VALUES(col) to get the value from the insert argument in case of duplicate. Eg: ON DUPLICATE UPDATE b = VALUES(b), c = VALUES(c)
    – gerrytan
    Commented May 20, 2014 at 1:51
7

I know this is an old question, but the Google lead me here recently so I imagine others come here, too.

@chaos is correct: there is the INSERT ... ON DUPLICATE KEY UPDATE syntax.

However, the original question asked about MySQL specifically, and in MySQL there is the REPLACE INTO ... syntax. IMHO, this command is easier and more straightforward to use for upserts. From the manual:

REPLACE works exactly like INSERT, except that if an old row in the table has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted before the new row is inserted.

Note this is not standard SQL. An example from the 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)

Edit: Just a fair warning that REPLACE INTO isn't like UPDATE. As the manual says, REPLACE deletes the row if it exists, then inserts a new one. (Note the funny "2 rows affected" in the example above.) That is, it will replace the values of all columns of an existing record (and not merely update some columns.) The behavior of MySQL's REPLACE INTO is much like that of Sqlite's INSERT OR REPLACE INTO. See this question for some workarounds if you only want to update a few columns (and not all columns) if the record already exists.

1
  • 1
    Old question, I know: but for anyone thinking of doing this; please keep in mind using the REPLACE INTO has a potential "gotcha." If you've got any "ON DELETE CASCADE" magic going on, this REPLACE will cause the cascade to trigger - possibly causing huge issues. See: stackoverflow.com/questions/4723145/…
    – nchopra
    Commented Aug 26, 2022 at 15:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.