我正在将自定义小部件添加到 Drupal 6.x 和 MySQL 5.5 的实例中,并遇到了更新行的问题。
我有一个配方成分表,其中多种成分通过节点 ID (nid) 和版本 ID (vid) 绑定到一个配方。
主键是 vid、nid 和 order。
vid & nid 与 recipe nid & vid 字段相关。
表架构是:
+-----------------+------------------+
| Field | Type |
+-----------------+------------------+
| vid | int(10) unsigned |
| nid | int(10) unsigned |
| order | int(10) unsigned |
| name | varchar(255) |
| unit_of_measure | varchar(32) |
| quantity | int(10) unsigned |
+-----------------+------------------+
当试图重新订购配料时,问题就来了。
例如:
+-----+-----+-------+---------+-------------------+----------+
| vid | nid | order | name | unit_of_measure | quantity |
| 5 | 1 | 1 | Chicken | Lb | 1 |
| 5 | 1 | 2 | Rice | Cup | 2 |
| 5 | 1 | 3 | Thyme | Tbsp | 3 |
+-----+-----+-------+---------+-------------------+----------+
我想将 Thyme 移到列表的顶部,但我无法将 Thyme 的顺序更改为 1,因为该主键已经存在(Chicken)。我不能将 Chicken 下调到 order 2,因为 Rice 已经在那里,等等......
我的立场是我们应该添加一个唯一的自动递增 int 字段,它将成为唯一的主键。这将使我们能够对行重新排序而不会发生两行可能以相同的 nid、vid 和顺序结束的可能性。
我的同事的立场是,添加一个独特的自动增量 int 字段是糟糕的设计,因为永远不应该有两个不同的行具有相同的 vid、nid 和顺序。但是遵循这种信念,有两种方法可以实现行的重新排序
- 使用较大的数字(即 1001、1002、1003)更新每一行的顺序,以便原始顺序不再冲突,然后使用正确的顺序值(1、2、3)更新每一行。
- 删除具有相同 nid 和 vid 的每一行,然后以正确的顺序再次插入所有行。
从数据库的角度来看,正确的做法是什么?
问题类似于这个 (SO) 问题:
How to swap values of two rows in mysql without violated unique constraint?
不幸的是,答案是一样的。由于 MySQL 检查
UNIQUE
(以及主键和外键)约束的方式,即逐行而不是在语句末尾或事务末尾检查,交换或重新排列作为唯一约束一部分的值不能轻易完成。我会同意你的第一个建议:
1. 用一些较大的数字(即 1001、1002、1003)更新每一行的顺序,以便原始顺序不再冲突,然后用正确的顺序值(1、2、3)更新每一行。
通过这种方式,您可以拥有执行更新所需的最少 (2) 条语句,并且读取 2 条语句之间的表的任何其他连接仍将读取一致状态的值。
为什么不将主键重新创建为 (nid, vid),并在“order”列上创建一个新索引(只是为了快速检索/ORDER BY 子句)。
--> 可能发生的最坏情况是有 2 种成分具有相同的“订单”值,但订单更改逻辑应该已经在应用程序中正确定义。