How to avoid unique index violations on updates in MySQL

There is a bug in MySQL that causes an UPDATE to fail with a unique index violation, even though the statement doesn’t create duplicate values. In this article I’ll explain when this bug can happen, and how to work around it.

The bug

This is easiest to demonstrate with SQL:

create table t (i int not null primary key);
insert into t(i) values (1), (2), (3), (4);
update t set i = i + 1;
-- ERROR 1062 (23000): Duplicate entry '2' for key 1

The bug is caused by MySQL’s method of updating the values. It updates the first row (in index order), then checks for index violations. Since there is now a duplicate row, it fails. The correct standards-compliant behavior would be to update all the rows, then check for violations, but that is much more difficult and less efficient, so MySQL does not follow the standard.

The workaround

The solution is to update the rows in a different order. MySQL allows an ORDER BY clause on UPDATE statements:

update t set i = i + 1 order by i desc;

Now the query updates 4 to 5, then 3 to 4, and so on, avoiding any conflicts.

More complex cases

There are cases where the workaround can’t be as simple as the above:

update t set i = case when i > 2 then i + 1 else i - 1 end;
-- ERROR 1062 (23000): Duplicate entry '4' for key 1
update t set i = case when i > 2 then i + 1 else i - 1 end order by i desc;
-- ERROR 1062 (23000): Duplicate entry '1' for key 1

I can’t find a foolproof way to work around this. Here’s one statement that works on this particular situation:

update t
   set i = case when i > 2 then i + 1 else i - 1 end
   order by case when i > 2 then 1000 - i else i end

Depending on the data, it might not be that easy. There are cases where no ordering can work at all, such as when swapping numbers:

update t
   set i = case when i = 1 then 2 else 1 end
   where i in (1,2);

I’ll write another post on swapping numbers in MySQL.

Beware a half-updated table

Unfortunately, this bug can sometimes cause the table to be left in an inconsistent state. In the more complex example above, if the table is InnoDB, the entire update is rolled back when it fails. If the table is MyISAM, half the updates are successful and half fail. This is the case with all updates or inserts that fail in MyISAM, since the storage engine is not transactional.

Technorati Tags:No Tags

You might also like:

  1. How to reverse a sequence in SQL
  2. An introduction to InnoDB error handling
  3. How to write flexible INSERT and UPDATE statements in MySQL
  4. How to implement a queue in SQL
  5. Maatkit version 1297 released

4 Responses to “How to avoid unique index violations on updates in MySQL”


  1. 1 Noah Winecoff

    Great write up. I develop everyday using MySQL and information like this makes my job a lot easier. Thanks.

  2. 2 Mark

    I was working on a project and ran into this exact problem, fortunately a google search turned up your article and it saved me. Thanks for the assist!

  3. 3 Simon Giddings

    Hi,

    I issue an update statement containing a where clause on the primary
    index, so as to update a single record. Howerver this is failing with
    “Duplicate entry ‘6′ for key 1″ -
    update clients.calendarentry set Subject = ‘presentation’ where
    idCalendarEntry = 6;

    In the table, the field ‘idCalendarEntry’ is declared as :
    `idCalendarEntry` int(10) unsigned NOT NULL auto_increment

    The server version of MySql I am using is 5.0.24
    The client version of MySql I am using is 5.0.11

    Are you able to help?
    Simon

  4. 4 Xaprb

    It looks like you might accidentally be trying to update the idCalendarEntry column — are you sure the query is being issued as you say? If so, that’s pretty bizarre.

Leave a Reply

Please do not use this blog to get help with problems or bugs in Maatkit or innotop: use the Sourceforge forums, mailing list, or bug trackers. If you're asking for help with MySQL, please use the MySQL mailing list instead. I'm writing a book and my time is extremely limited :-)