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
Great write up. I develop everyday using MySQL and information like this makes my job a lot easier. Thanks.
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!
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
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.