繁体   English   中英

MySQL:避免在更新之前必须在 UNIQUE 字段上设置 NULL 值

[英]MySQL: Avoid having to set a NULL value on an UNIQUE field before updating it

我有一个 SQL 表,其中包含两个字段: idorder

CREATE TABLE IF NOT EXISTS article (
  `id`        INT(11) NOT NULL AUTO_INCREMENT,
  `order`     INT(11) UNIQUE,
  PRIMARY KEY (`id`)
);

在这张表中,我有一些项目:

+----+-------+
| id | order |
+----+-------+
|  1 |     0 |
|  2 |     1 |
|  3 |     2 |
|  4 |     3 |
|  5 |     4 |
|  6 |     5 |
|  7 |     6 |
|  8 |     7 |
|  9 |     8 |
| 10 |     9 |
+----+-------+

Now I want to change order position of one item: element with id 3 (order position 2) will move into position 6. Thus, the elements between position 4 and 6 (this last one included) will have to decrease their order field. 结果应该是这样的:

+----+-------+
| id | order |
+----+-------+
|  1 |     0 |
|  2 |     1 |
|  4 |     2 | ⌉
|  5 |     3 | |
|  6 |     4 | | Updated items
|  7 |     5 | |
|  3 |     6 | ⌋
|  8 |     7 |
|  9 |     8 |
| 10 |     9 |
+----+-------+

当然,第一次更新——用order字段6更新 id 为3的商品——很简单:

UPDATE article
SET article.order = 6
WHERE id = 1;

然后我可以减少位置大于 2 和低于或 euql 小于 6 之间的项目:

UPDATE article
SET article.order = article.order -1
WHERE 
  article.order > 2
  AND
  article.order <= 6
;

但是这里有一个问题: order字段是UNIQUE 所以我必须首先将它设置为NULL来移动我要移动的项目:

UPDATE article
SET article.order = NULL
WHERE id = 3;

UPDATE article
SET article.order = article.order -1
WHERE 
  article.order > 2
  AND
  article.order <= 6
;


UPDATE article
SET article.order = 6
WHERE id = 3;

有没有办法避免设置这个NULL 这是一个小提琴: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=342d714e88e68d3c4c6e0ca1ec8efa6c

它可以在 2 次更新中完成。

在一些变量的帮助下。
还有一种降序的否定形式。

--
-- move id 3 to position 6
--
SET @id := 3;
SET @orig := (select `order` from article where id = @id);
SET @dest := 6;

UPDATE article 
SET `order` = NULL
WHERE id = @id;

UPDATE article 
SET `order` =  
    case 
    when `order` between @orig and @dest then `order` - 1
    when `order` is null then @dest
    else `order`
    end
WHERE (`order` is null OR `order` between @orig and @dest) 
ORDER BY -`order` DESC;

关于db<>fiddle 的演示在这里

有没有办法避免设置这个 NULL?

不在 MySQL 中。 SQL 标准定义了解决此特定问题的可延迟约束的特性。 不幸的是,MySQL 没有实现 SQL 规范的这一部分。 Null 是您唯一的选择。

现在,当一个约束被标记为可延迟时(如在 PostgreSQL 或 Oracle 中),它的验证可以推迟到每个 SQL 语句执行结束,甚至整个事务结束; 也就是说,它的完整验证仅在commit时发生,在所有更新都已完成并且所有值都会再次良好之后。

如您所见,除非您可以选择迁移到 PostgreSQL 或 Oracle(极不可能),否则您将无法使用空值。

我会问自己是否真的需要唯一约束? 是否有任何功能依赖于唯一的订单价值? 约束的额外开销是否必要? 如果不 -

UPDATE `article`
SET `order` = IF(`id` = 3, 6, `order` - 1)
WHERE `order` BETWEEN 2 AND 6;

db<>小提琴

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM