避免原子事务中的唯一违规

时间:2016-06-10 19:25:19

标签: mysql

可以在MySQL中创建原子事务吗?

考虑一下我有表'类别'这些行:

id|name
--|---------
1 |'tablets'
2 |'phones'

name是我的主键。

如果我尝试:

START TRANSACTION;
update "category" set name = 'phones' where id = 1;
update "category" set name = 'tablets' where id = 2;
COMMIT;

我得到了:

ERROR:  duplicate key value violates unique constraint 
"category_name_key"
DETAIL:  Key (name)=(tablets) already exists.

我的期望是只能在提交期间进行约束检查。这可能与MySQL有关吗?

3 个答案:

答案 0 :(得分:1)

是不可能的,因为mysql MySQL进程更新在每次单行更新后强制执行UNIQUE(和其他)约束的检查,而不是 - 就像它应该做的那样 - 在完成整个UPDATE语句之后。

然后你应该使用中间更新

update "category" set name = 'temp' where id = 1;
update "category" set name = 'tablets' where id = 2;
update "category" set name = 'phone' where id = 1;

答案 1 :(得分:1)

目前在MySQL中没有这样的东西。 根据mysql doc

  

与MySQL一般,在插入,删除或删除的SQL语句中   更新许多行,InnoDB检查UNIQUE和FOREIGN KEY约束   行由行。执行外键检查时,InnoDB设置共享   它必须查看的子记录或父记录上的行级锁定。 InnoDB的   立即检查外键约束;支票不会延期   到事务提交。根据SQL标准,默认   行为应该推迟检查。也就是说,只有约束   在处理完整个SQL语句后检查。直到   InnoDB实现了延迟约束检查,有些事情将是   不可能,例如删除使用a引用自身的记录   外键。

要在Mysql中使用中间表执行此操作,以便同样的问题不会发生,但目前在mysql中没有干净的方法。

它可以在其他数据库中使用,例如Postgresql,您可以在事务提交后设置模式以检查约束

SET CONSTRAINTS { ALL | name [, ...] } { DEFERRED | IMMEDIATE }

答案 2 :(得分:1)

你使这个例子过于简单,所以即使这解决了你的问题,也许不是你想要的。

<强> SQL Fiddle Demo

UPDATE `category`
SET id =  CASE WHEN name = 'phones'   THEN 1
               WHEN name = 'tablets'  THEN 2
          END
WHERE name in ('tablets', 'phones');

<强>输出

| id |    name |
|----|---------|
|  2 | tablets |
|  1 |  phones |