MySQL: INSERT IF NOT EXISTS syntax
18th October 2007
To start: as of the latest MySQL, syntax presented in the title is not possible. But there are several very easy ways to accomplish what is expected using existing functionality.
There are 3 possible solutions: using INSERT IGNORE, REPLACE, or INSERT … ON DUPLICATE KEY UPDATE.
Imagine we have a table:
- CREATE TABLE `transcripts` (
- `ensembl_transcript_id` varchar(20) NOT NULL,
- `transcript_chrom_start` int(10) unsigned NOT NULL,
- `transcript_chrom_end` int(10) unsigned NOT NULL,
- PRIMARY KEY (`ensembl_transcript_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Now imagine that we have an automatic pipeline importing transcripts meta-data from Ensembl, and that due to various reasons the pipeline might be broken at any step of execution. Thus, we need to ensure two things: 1) repeated executions of the pipeline will not destroy our database, and 2) repeated executions will not die due to ‘duplicate primary key’ errors.
Method 1: using REPLACE
It’s very simple:
- REPLACE INTO `transcripts`
- SET `ensembl_transcript_id` = 'ENSORGT00000000001',
- `transcript_chrom_start` = 12345,
- `transcript_chrom_end` = 12678;
If the record exists, it will be overwritten; if it does not yet exist, it will be created.
However, using this method isn’t efficient for our case: we do not need to overwrite existing records, it’s fine just to skip them.
Method 2: using INSERT IGNORE
Also very simple:
- INSERT IGNORE INTO `transcripts`
- SET `ensembl_transcript_id` = 'ENSORGT00000000001',
- `transcript_chrom_start` = 12345,
- `transcript_chrom_end` = 12678;
Here, if the ‘ensembl_transcript_id’ is already present in the database, it will be silently skipped (ignored). (To be more precise, here’s a quote from MySQL reference manual: “If you use the IGNORE keyword, errors that occur while executing the INSERT statement are treated as warnings instead. For example, without IGNORE, a row that duplicates an existing UNIQUE index or PRIMARY KEY value in the table causes a duplicate-key error and the statement is aborted.”.) If the record doesn’t yet exist, it will be created.
This second method has several potential weaknesses, including non-abortion of the query in case any other problem occurs (see the manual). Thus it should be used if previously tested without the IGNORE keyword.
There is one more option: to use INSERT … ON DUPLICATE KEY UPDATE syntax, and in the UPDATE part just do nothing do some meaningless (empty) operation, like calculating 0+0 (Geoffray suggests doing the id=id assignment for the MySQL optimization engine to ignore this operation). Advantage of this method is that it only ignores duplicate key events, and still aborts on other errors.
As a final notice: this post was inspired by Xaprb. I’d also advise to consult his other post on writing flexible SQL queries.
April 20th, 2008 at 8:19
[...] may also find http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html good reading on the [...]
June 23rd, 2008 at 14:09
Very interesting, and helpful.
July 13th, 2008 at 12:53
Thanks!
July 22nd, 2008 at 12:38
Thank you very much!!
August 4th, 2008 at 13:41
Very useful, thank you! How about insert, update and delete in a single query, it’s possible? Something like:
insert ignore into table (field_one, field_two) values (values_one, values_two) on duplicate key update field_one = values(values_one), field_two = values(values_two) THEN QUERY FOR DELETE HERE?
Thanks
August 4th, 2008 at 19:25
Teo,
As I didn’t do anything similar before, I can’t offer you a ready working solution.
But you should just try your query . The part before THEN should work (unless “insert ignore” is not compatible with “on duplicate key”). For THEN-part, I suspect different syntax might be needed – look for MySQL conditional statements IF-THEN-ELSE.
August 28th, 2008 at 14:39
About your last option, I don’t think it’s possible to do nothing after the “ON DUPLICATE KEY UPDATE”. What you can do is updating the column (PRIMARY KEY or/and UNIQUE) which generate a duplicate entry error with it’s own value. I think MySQL will ignore the update because these values are similar, so it could also be a quite fast solution.
INSERT INTO `test`(id) VALUES (1) ON DUPLICATE KEY UPDATE id=id
August 30th, 2008 at 16:21
Geoffray,
if the MySQL optimization engine really does that, and if this is allowed (remember, we are triggering the ON DUPLICATE KEY event, and then trying to assign that key’s value to another value, even if it is the same), then this is a really good solution.
Based on your comment, I updated the post, as MySQL syntax in fact doesn’t allow the empty statement after ON (…) UPDATE.
September 29th, 2008 at 16:04
Would this work on InnoDB with composite keys as well? My task is to read only new entries from a log and insert them into a MySQL table. The table has a composite key.
(I could try this myself, but I have no MySQL box to test on for the next few weeks.)
September 29th, 2008 at 21:55
Marius,
neither MySQL documentation, nor my (not so extensive) experience with MySQL raise any support for disabling described functionality for composite keys. As I understand, composite key is still a single key, and behaves as such. And MySQL documentation sometimes mentions that composite keys are just concatenated respective fields (columns).
Actually, it might be that I did apply the REPLACE or IGNORE approach to a composite-keyed table (and it was InnoDB at that time, now for that project I’m using MyISAM to save space). But I’m unsure – didn’t check the code since it was written (now almost a year ago).
October 29th, 2008 at 11:24
thanks. it is very useful. Sometimes we want to send pop-up messagebox when we changed or ignord the same record. Your solutions are good but I want to know how to inform user that we changed or ignored the record. For example: “Customer couldn’t be added. Because the customer you entered is already exists.” So how can I determine whether users attempt is executed or ignored?
October 29th, 2008 at 13:01
Cakirhal,
first of all, you will need to consult MySQL documentation for the exact queries you are using – the “returned rows”/”affected rows” count might be helpful.
Also, before executing the actual modification query, you can execute selection query (i.e. “SELECT … FROM”) in your program, to know for sure if the customer exists. After that, it is a trivial task to notify the user of any problems.
January 29th, 2009 at 16:41
Thank you for the concise explanation.
June 24th, 2009 at 19:33
thank you for your help, you are the best
June 24th, 2009 at 22:09
Thanks, this article is very helpful!
August 5th, 2009 at 2:45
I have a table A (col1 INT, col2 VARCHAR(1), col3 VARCHAR(1)) where col1 is primary key
i’ve a record A(1, ‘A’,null)
I want to update/insert a records if exists/not-exists to get a result of A(1, ‘A’, ‘B’);
I can do this using update A set col2=’B’ where col1=1; But, I do not know whether col1=1 exists or not
what shuld i do?
August 8th, 2009 at 16:28
Dollan, have you actually read the post text? Either I do not understand your problem, or it can indeed be solved using either INSERT IGNORE or REPLACE INTO.
September 23rd, 2009 at 20:52
Great Post, thanks!
I am thinking that INSERT IGNORE would not overwrite like REPLACE would, should one want to insert the same text with different parameters. I will definitely try that now.
Regards
October 26th, 2009 at 1:31
Thanks for good explanation.
November 7th, 2009 at 2:28
Thanks! Glad to know I’m not the only one thinking this way.
November 27th, 2009 at 20:50
[...] ง่ายๆ เท่าที่à¸à¹ˆà¸²à¸™à¸¡à¸²à¸ˆà¸²à¸ http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html ผ่านทาง [...]
December 1st, 2009 at 8:16
Fantastic!!! you saved my frazzled brain. if not exists was tormenting me.
March 9th, 2010 at 17:07
I did some speed benchmarking of these methods and found that ON DUPLICATE KEY is the faster that INSERT IGNORE. I also found that just letting the query fail because of a primary key constraint is faster than both. For my application, anyway. Your mileage may vary. More info in my “website” link.
March 9th, 2010 at 17:46
Thanks, that was an interesting comparison. I only had tens of thousands of rows, so didn’t pay attention to performance.
April 22nd, 2010 at 8:53
REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;
It’s important to note that unless you mention all fields and have autogenerated fields (such as autoincremented primary key), you get to screw up your DB with this method:
I tried
REPLACE INTO images SET filename='variable'
and guess what, since a record existed, its autoincremened ID got changed.
So watch out for such mistakes: my error may be your lesson.
June 11th, 2010 at 20:29
Also i’m using
INSERT INTO `table` (value1,value2) SELECT ‘stuff for value1′,’stuff for value2′ WHERE NOT EXISTS (SELECT * FROM table WHERE value1=’stuff for value1′ AND value2=’stuff for value2′) LIMIT 1
June 12th, 2010 at 1:24
Using sub-query most likely will be slower than all the other solutions (because sub-queries will use temporary tables).
Also, MySQL INSERT…SELECT manual page says: However, you cannot insert into a table and select from the same table in a subquery. Thus, your solution may not work at all.
July 16th, 2010 at 8:12
Hi!
I have a note table, each note has a respective x and y positions..and everytime i add/create a new note, the newly added note should have a unique x and y values in the db..can you suggest on how to do this on sql?
July 16th, 2010 at 14:04
MySQL-specific solution would be to use autoincrement (linked page also has an example for a two-component primary key, something like your X and Y; see also comments there for more detailed explanation).
Actually, do think if you really want those X and Y serve the purpose of unique IDs… maybe it is better having a single ID, and storing X and Y as values?
In any case, you could implement X/Y-incrementing logic in your app completely DB-engine independently, e.g.
X.new = SELECT MAX(X) + 1 FROM table;
Y.new = SELECT MAX(Y) + 1 FROM table;
INSERT INTO table(X, Y, note) VALUES (X.new, Y.new, note);
However, in this case you should be aware of possible race conditions, if there are several processes possibly writing to that table. To avoid that, you will need table locking. Some apps (e.g. Gallery2) maintain separate tables (sequences) of incremental IDs, to avoid locking data-tables in favour of locking only sequence-tables…
Better use a single autoincrement ID
September 8th, 2010 at 17:19
Very nice article, really useful
September 19th, 2010 at 6:42
wundervoll! thanks for this
September 30th, 2010 at 7:17
nice thank u!!!
February 18th, 2011 at 5:54
Note that the mysql doc states:
If the statement updates a row instead, LAST_INSERT_ID() is not meaningful. However, you can work around this by using LAST_INSERT_ID(expr). Suppose that id is the AUTO_INCREMENT column. To make LAST_INSERT_ID() meaningful for updates, insert rows as follows:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), c=3;
August 29th, 2011 at 6:22
Thanks man.. first result on google, no need to go anywhere else
September 9th, 2011 at 19:29
cheers – just saved me a real headache. Was considering doing a SELECT for every record, checking for existence, then making a decision based on that… man, sometimes I go round the houses when a simple solution is right there in front of me.
September 20th, 2011 at 19:37
You are my Hero!
September 29th, 2011 at 13:40
[...] http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html [...]
October 14th, 2011 at 15:19
I have created a page that will automatically add posts to WordPress but every time it runs, it creates duplicate posts. The code string is below. I’ve tried several variations to get the code to update the record if it has changed, otherwise ignore it and add any new records. Any help would be appreciated.
$link = mysql_connect($hostname, $user, $pass);
mysql_select_db($dbname, $link);
$results = mysql_query(“SELECT * FROM genesis”,$link);
require(‘./wp-load.php’);
November 21st, 2011 at 13:23
Thanks for info:INSERT IGNORE INTO table_name SET col = ‘value’;
Still, table shall be updated as:
ALTER TABLE `db_name`.`table_name` ADD UNIQUE KEY(col);
(otherwise, the first statement won’t work.)
All the best,
November 29th, 2011 at 21:03
Thanks dude..
After struggling for couple of hours I found this “INSERT IGNORE INTO”.
Thanks A TON
December 16th, 2011 at 22:35
INSERT into Foo.bar select “va11″, “val2″ from dual where not exists (select val1 from Foo.bar where username = “val1″);
January 3rd, 2012 at 12:06
“Insert Ignore into” increments auto number even in cases that new record is not added.
March 30th, 2013 at 11:21
[...] Bogdan has the answers . [...]
August 31st, 2013 at 9:09
Very useful and best information, only thing is that site fonts are very small. We cannot read it. INSERT IGNORE INTO `transcripts`
SET `ensembl_transcript_id` = ‘ENSORGT00000000001′,
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;
I hope you understand what I mean, ignore my comment just make the changes.
Thank you.
August 31st, 2013 at 16:07
I’ve increased font size a little, should be better now. What is your screen resolution? Is it higher than 1920×1080 or 1920×1200?
June 3rd, 2014 at 5:47
[…] see http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html […]