1142

I want to add a row to a database table, but if a row exists with the same unique key I want to update the row.

For example:

INSERT INTO table_name (ID, NAME, AGE) VALUES(1, "A", 19);

Let’s say the unique key is ID, and in my Database, there is a row with ID = 1. In that case, I want to update that row with these values. Normally this gives an error.
If I use INSERT IGNORE it will ignore the error, but it still won’t update.

informatik01
  • 16,038
  • 10
  • 74
  • 104
Keshan
  • 14,251
  • 10
  • 48
  • 72
  • 15
    SQL needs an official syntax for this use case that doesn't force duplication of values in the syntax and preserves the primary key. – Pete Alvin Jan 26 '18 at 13:02
  • 1
    To get the influenced id refer to [MySQL ON DUPLICATE KEY - last insert id?](https://stackoverflow.com/q/778534/6521116) – LF00 Apr 24 '19 at 06:30
  • 1
    **Caveat:** as of version 5.7 this approach does not directly support WHERE clause as part of the INSERT/UPDATE operation. Also, an UPDATE actually counts as two separate operations (DELETE and INSERT) ... in case that matters for audit purposes. (Learnbit) – dreftymac Jul 10 '19 at 21:01

12 Answers12

2057

Use INSERT ... ON DUPLICATE KEY UPDATE

QUERY:

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name="A", age=19
Donnie
  • 45,732
  • 10
  • 64
  • 86
  • 120
    +1 From what I've found, this method is less problematic for auto-increment keys and other unique key collisions than REPLACE INTO, and it is more efficient. – Andrew Ensley May 11 '12 at 21:27
  • 65
    I know all of you allude to this, but I want to be explicit for others. If the ID you insert is NOT the PRIMARY KEY or UNIQUE, then this will not work. This didn't initially work for me because my ID was not unique. – neuquen Dec 04 '13 at 21:12
  • 20
    This is a bit late, but anyway: it is stated in the [manual](https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html]) that updates in `ON DUPLICATE KEY UPDATE` increase the affected rows by 2. It reports 0 if nothing is actually updated (same as the regular `UPDATE`). – Vatev Mar 31 '14 at 11:53
  • 89
    Also note that you can use VALUES (name) to reference to the value you attempt to insert e.g. INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE name=VALUES(name) , age=VALUES(age) – Curious Sam Mar 01 '16 at 10:07
  • 6
    Unfortunately `ON DUPLICATE KEY UPDATE` causes the autoincrement to increment even on updates, making this pretty much unusable if you need to do hundreds or thousands of these queries per day, since then the autoincrement will steadily increase by x rows every time even if no new rows are being added. – twhitney Mar 22 '22 at 17:52
  • 2
    Another useful tip when using this in MySQL/MariaDB is the `INSERT INTO table SET col = val` syntax, allows for copy and pasting between the 2 parts of the query. so the above would be: `INSERT INTO table SET id = 1, name = "A", age = 19) ON DUPLICATE KEY UPDATE name = "A", age = 19` – mike16889 May 09 '22 at 03:39
  • For MySQL 8.0.20+ VALUES() is now deprecated and will be removed. Use aliases. Check the manual https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html – El Gucs Mar 28 '23 at 00:01
325

Check out REPLACE:

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.

Example:

REPLACE INTO `tablename` (`id`, `name`, `age`) VALUES (1, "A", 19)
Flimm
  • 136,138
  • 45
  • 251
  • 267
Martin Schapendonk
  • 12,893
  • 3
  • 19
  • 24
  • 17
    @Piontek Because this one is shorter and easier to understand and no-one explained why "insert on duplicate" is better. – Mr_Chimp Jun 13 '13 at 13:55
  • 106
    it changes the IDs of the record and thus may destroy foreign references. – boh Sep 13 '13 at 05:32
  • 138
    The other problem with REPLACE INTO is that you _must_ specify values for ALL fields...otherwise fields will get lost or replaced with default values. REPLACE INTO essentially **deletes** the row if it exists, and inserts the new row. In the example, if you did 'REPLACE INTO table (id, age) values (1, 19) then the name field would become null. – Dale Dec 08 '13 at 08:05
  • 48
    This is actually DELETE the entire row and perform new INSERT. – mjb Jun 12 '14 at 04:05
  • 7
    all the comments here are true but... this might be exactly what is needed sometimes – IceFire Feb 08 '22 at 20:42
  • 1
    @IceFire "But sometimes" is a bad reason to use something as the general approach. – DennisK Mar 10 '22 at 07:06
76

When using batch insert use the following syntax:

INSERT INTO TABLE (id, name, age) VALUES (1, "A", 19), (2, "B", 17), (3, "C", 22)
ON DUPLICATE KEY UPDATE
    name = VALUES (name),
    ...
user247702
  • 23,641
  • 15
  • 110
  • 157
Fabiano Souza
  • 930
  • 7
  • 5
  • 1
    If `VALUES(name)` does not work, you can try `name = 'name_value',...;` – Sunfarm Apr 21 '20 at 06:23
  • 9
    VALUES is now deprecated - you should use insert into TABLE (Id, name, age) values (1, "A", 19), (2, "B", 20) as ins on duplicate key update name=ins.name, age=ins.age; – Liam Jun 04 '21 at 07:37
  • 1
    all the name will be updated to same value – Umair Ayub Dec 02 '21 at 11:47
50

Any of these solution will work regarding your question:

INSERT IGNORE INTO table (id, name, age) VALUES (1, "A", 19);

or

INSERT INTO TABLE (id, name, age) VALUES(1, "A", 19) 
    ON DUPLICATE KEY UPDATE NAME = "A", AGE = 19;  

or

REPLACE INTO table (id, name, age) VALUES(1, "A", 19);
Dilraj Singh
  • 951
  • 10
  • 12
  • 10
    I downvoted because this doesn’t explain the differences between these solutions and the drawbacks (if any) of each one. – bfontaine Aug 18 '22 at 12:43
  • 2
    I don't believe the first version produces the same results as the others. As I understand it, "IGNORE" will simply cause the DB to treat the conflict as a warning instead of an error. It will not update the conflicting row. Further, as others have pointed out, REPLACE INTO is effectively a DELETE/INSERT whereas ON DUPLICATE KEY will update an existing row. – Greg Brown Jan 15 '23 at 15:48
26

Try this:

INSERT INTO table (id,name,age) VALUES('1','Mohammad','21') ON DUPLICATE KEY UPDATE name='Mohammad',age='21'

Note:
Here if id is the primary key then after first insertion with id='1' every time attempt to insert id='1' will update name and age and previous name age will change.

Atur
  • 1,712
  • 6
  • 32
  • 42
Rasel
  • 5,488
  • 3
  • 30
  • 39
  • I want to work without using *id*. Have you tried without primary key? – ersks Jul 06 '17 at 06:41
  • 1
    @ersks see the question. user asked about when there is an unique key – Rasel Jul 09 '17 at 08:44
  • I got that, but I am trying to solve my problem in which these only values are know. So, in such situation I wrote above comment hoping correct solution. – ersks Jul 13 '17 at 08:33
25

Try this out:

INSERT INTO table (id, name, age) VALUES (1, 'A', 19) ON DUPLICATE KEY UPDATE id = id + 1;

Hope this helps.

Luis Reyes
  • 374
  • 2
  • 7
  • 6
    actually i don't need to add the new values to another row with a new ID instead i want to replace the existing values of id = 1 with this values. (as i understand this increments the id and add the data) – Keshan Nov 17 '10 at 14:21
  • 61
    I don't think he wants to increase the id by one on duplicates. – Donnie Nov 17 '10 at 14:22
  • 7
    "transgress" is not the word you're looking for :) Unfortunately, now I've seen "transgress", I can no longer visualise the actual word.. – mwfearnley Apr 13 '17 at 07:47
  • Updating an ID like this might mess with its sequence or get an error if there is an existing id+1 – El Gucs Mar 27 '23 at 23:50
22

In case that you wanted to make a non-primary fields as criteria/condition for ON DUPLICATE, you can make a UNIQUE INDEX key on that table to trigger the DUPLICATE.

ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`);

And in case you want to combine two fields to make it unique on the table, you can achieve this by adding more on the last parameter.

ALTER TABLE `table` ADD UNIQUE `unique_index`(`name`, `age`);

Note, just make sure to delete first all the data that has the same name and age value across the other rows.

DELETE table FROM table AS a, table AS b WHERE a.id < b.id 
AND a.name <=> b.name AND a.age <=> b.age;

After that, it should trigger the ON DUPLICATE event.

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name = VALUES(name), age = VALUES(age)
Rich
  • 3,928
  • 4
  • 37
  • 66
  • I have tried that, I am gettting an error that says "BLOB/TEXT column 'column_name' used in key specification without a key length" – Natalia Oct 21 '20 at 14:32
  • 1
    @Natalia kindly create a [dbfiddle](https://dbfiddle.uk/) so I can check – Rich Oct 21 '20 at 14:34
  • 1
    I will try, but basically, you can not make text type of a column unique in MySQL. I made it varchar. – Natalia Oct 21 '20 at 15:22
  • "DELETE table FROM table AS a, table AS b WHERE a.id < b.id AND a.name <=> b.name AND a.age <=> b.age;" gives me error "Unknown table 'table' in MULTI DELETE" – Nosajimiki Oct 26 '21 at 20:31
17

Just because I was here looking for this solution but for updating from another identically-structured table (in my case website test DB to live DB):

INSERT  live-db.table1
SELECT  *
FROM    test-db.table1 t
ON DUPLICATE KEY UPDATE
        ColToUpdate1 = t.ColToUpdate1,
        ColToUpdate2 = t.ColToUpdate2,
        ...

As mentioned elsewhere, only the columns you want to update need to be included after ON DUPLICATE KEY UPDATE.

No need to list the columns in the INSERT or SELECT, though I agree it's probably better practice.

SteveCinq
  • 1,920
  • 1
  • 17
  • 22
16

When using SQLite:

REPLACE into table (id, name, age) values(1, "A", 19)

Provided that id is the primary key. Or else it just inserts another row. See INSERT (SQLite).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
DawnSong
  • 4,752
  • 2
  • 38
  • 38
  • What `replace into` does is exactly "insert into, or update when existing". @Owl – DawnSong Nov 17 '16 at 15:38
  • +1 ( 1 of 3 ) I found this to work for my situation - I needed to replace an existing row with a unique key or if not there then add the row. Simplest of solutions here. – therobyouknow Jan 30 '17 at 01:21
  • ( 2 of 3 ) My query: `CREATE TRIGGER worklog_update AFTER INSERT ON worklog FOR EACH ROW REPLACE INTO support_time_monthly_hours ( ProjectID, monthTotalTime, Year, Month ) SELECT jiraissue.PROJECT, SUM(worklog.timeworked), YEAR(CURRENT_DATE()), MONTH(CURRENT_DATE()) FROM worklog, jiraissue WHERE worklog.issueid = jiraissue.ID AND jiraissue.PROJECT = (SELECT PROJECT FROM jiraissue WHERE NEW.issueid = jiraissue.ID ) AND worklog.startdate BETWEEN DATE_FORMAT(NOW() ,'%Y-%m-01 00:00:00') AND NOW();` – therobyouknow Jan 30 '17 at 01:22
  • ( 3 of 3 ) Related question/answer http://stackoverflow.com/questions/41767517/insert-row-into-mysql-table-result-from-query-into-one-field-and-sql-function-re – therobyouknow Jan 30 '17 at 01:23
  • 2
    The problem with this in mysql is that replace will remove other values in case they are not provided. However @fabiano-souza solution is more appropriate – Quamber Ali Mar 21 '18 at 13:37
6

In case, you want to keep old field (For ex: name). The query will be:

INSERT INTO table (id, name, age) VALUES(1, "A", 19) ON DUPLICATE KEY UPDATE    
name=name, age=19;
Xman Classical
  • 5,179
  • 1
  • 26
  • 26
5

In my case i created below queries but in the first query if id 1 is already exists and age is already there, after that if you create first query without age than the value of age will be none

REPLACE into table SET `id` = 1, `name` = 'A', `age` = 19

for avoiding above issue create query like below

INSERT INTO table SET `id` = '1', `name` = 'A', `age` = 19 ON DUPLICATE KEY UPDATE `id` = "1", `name` = "A",`age` = 19

may it will help you ...

Renish Gotecha
  • 2,232
  • 22
  • 21
  • 6
    Does anyone know why we must assign the values twice? Why doesn't MySQL allow us to end the query at ON DUPLICATE KEY UPDATE without duplicating all the assignment statements? Some database tables have many columns, and this seems redundant / gratuitous. I understand why we have the option for alternate assignments, but why not have the option to omit them as well? Just curious if anyone knows. –  May 15 '19 at 13:50
4

Following are some of the possible approaches:

Using INSERT INTO

The INSERT statement allows you to insert one or more rows into a table

  • First, specify the table name and a list of comma-separated columns inside parentheses after the INSERT INTO clause.
  • Secondly, put a comma-separated list of values of the corresponding columns inside the parentheses following the VALUES keyword.
INSERT INTO table_name(column_name1, column_name2, column_name3) VALUES("col_value_1", "col_value_2", "col_value_3");

Using INSERT INTO with WHERE NOT EXISTS clause

INSERT INTO table_name (column_name_1, column_name_2, column_name_3)
SELECT * FROM (SELECT "col_value_1", "col_value_2","col_value_3") AS tmp_name
WHERE NOT EXISTS (
    SELECT column_name2 FROM table_name WHERE column_name = "sample_name"
) LIMIT 1;

Using REPLACE INTO

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.

REPLACE INTO table_name(column_name1, column_name2, column_name3) VALUES("col_value_1", "col_value_2", "col_value_3");
samnoon
  • 1,340
  • 2
  • 13
  • 23