如何在不违反唯一约束的情况下交换MySQL中两行的值?

wan*_*ist 38 mysql

我有一个带有优先级列的"任务"表,该列具有唯一约束.

我正在尝试交换两行的优先级值,但我一直违反约束.我在类似的情况下看到了这个声明,但它不是MySQL.

UPDATE tasks 
SET priority = 
CASE
    WHEN priority=2 THEN 3 
    WHEN priority=3 THEN 2 
END 

WHERE priority IN (2,3);
Run Code Online (Sandbox Code Playgroud)

这将导致错误:

Error Code: 1062. Duplicate entry '3' for key 'priority_UNIQUE'
Run Code Online (Sandbox Code Playgroud)

是否有可能在不使用虚假值和多个查询的情况下在MySQL中完成此操作?

编辑:

这是表结构:

CREATE TABLE `tasks` (
  `id` int(11) NOT NULL,
  `name` varchar(200) DEFAULT NULL,
  `priority` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `priority_UNIQUE` (`priority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Run Code Online (Sandbox Code Playgroud)

ype*_*eᵀᴹ 30

是否有可能在不使用虚假值和多个查询的情况下在MySQL中完成此操作?

不.(我无法想到).

问题是MySQL如何处理更新.MySQL(与UPDATE正确实现的其他DBMS不同)以破碎的方式处理更新.它UNIQUE在每个单行更新后强制检查(和其他)约束,而不是 - 在整个UPDATE语句完成后 - 应该这样做 - .这就是为什么你(大多数)其他DBMS没有这个问题.

对于某些更新(如增加全部或某些ID id=id+1),可以通过使用 - 更新中的另一个非标准功能来解决此问题ORDER BY.

为了交换两行中的值,这个技巧无济于事.您将不得不使用NULL或伪造的值(不存在但在您的列中允许)和2或3个语句.

您也可以暂时删除唯一约束,但我认为这不是一个好主意.


因此,如果唯一列是有符号整数且没有负值,则可以使用事务中包含的2个语句:

START TRANSACTION ;
    UPDATE tasks 
    SET priority = 
      CASE
        WHEN priority = 2 THEN -3 
        WHEN priority = 3 THEN -2 
      END 
    WHERE priority IN (2,3) ;

    UPDATE tasks 
    SET priority = - priority
    WHERE priority IN (-2,-3) ;
COMMIT ;
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很棒的答案......虽然不是我想听的人!多么皇家的PITA! (3认同)