【问题标题】:MySQL updating sort_order fields with unique constraints causes unique constraint violationMySQL 使用唯一约束更新 sort_order 字段会导致违反唯一约束
【发布时间】:2015-12-17 14:44:42
【问题描述】:

我有这个表结构:

表 1:id, group_id, label, sort_order

密钥:PK: id, UNIQUE: group_id+sort_order, INDEX: group_id

样本数据:

id   group_id   label       sort_order
50   1          Field 1     1 
51   1          Field 2     2
52   1          Field 3     3

(group_id, sort_order) 是唯一键。

为了更新前端记录的排列,sort_order 字段用于 order by 子句。用户可以通过在前端拖动标签来更新订单,这会传递 id,例如:52, 51,50,然后将其视为新订单/排序。

预期结果:

id   group_id   label       sort_order
50   1          Field 1     3 
51   1          Field 2     2
52   1          Field 3     1

该过程应使用新的 sort_order 值更新所有 3 条记录。更新查询示例:

UPDATE Table1 SET sort_order = 1 WHERE id = 52 AND group_id = 1

但是,由于 sort_order 是唯一键的一部分,因此由于 group_id,sort_order 键已存在,因此会引发错误。

我做了一个骇人听闻的解决方法,我两次更新排序顺序,例如:通过将其更新为更高的值,例如:sort_order = actual_order + 1000,然后使用实际值再次更新,例如:sort_order = actual_order

更好的方法是什么?我应该完全删除唯一键吗?

--

编辑

--

为了更好地了解问题,这是我的 PHP 代码:

示例字段 ID:52,51,5050,51,5251,52,50。顺序决定了新的排序方式。

public function updateSort($projectId, $groupId, $subgroupId, array $fieldIds)
{
    // Other stuff
    // ...

    $statement = create_a_prepared_statement(); // ...

    // Updating sort_order directly would result to unique key violation
    // so change the sort_order into some negative numbers and re-sort with
    // the actual orders
    foreach ($fieldIds as $sortIndex => $fieldId) {
        $sort = $sortIndex - 1000;
        $statement->execute(array(
            'id' => $fieldId,
            'project_id' => $projectId,
            'group_id' => $groupId,
            'subgroup_id' => $subgroupId,
            ':0' => $sort,
        ));
    }

    // Now sort it correctly
    foreach ($fieldIds as $sortIndex => $fieldId) {
        $sort = $sortIndex + 1;
        $statement->execute(array(
            'id' => $fieldId,
            'project_id' => $projectId,
            'group_id' => $groupId,
            'subgroup_id' => $subgroupId,
            ':0' => $sort,
        ));
    }

    return true;
}

【问题讨论】:

  • 有什么原因不能将 sort_order 移出 PK 并将实际的唯一 ID 放入其中吗?
  • 您的 hack 是一种合理的方法。我通常使用负值,因此如果出现问题,它们会立即显现出来。另一种可能性是使用NULL 值。
  • @MutuYolbulan 。 . . OP声明该组合是唯一键,而不是主键。我猜id 是主键。
  • @MutuYolbulan group_id+sort_order 是除自动增量 ID 之外的附加唯一键。更新了问题以添加密钥详细信息。
  • 如果您必须将分组和排序设为唯一,除非您在更新之前删除整个约束然后将其放回,否则您的 hack 将是唯一的方法,我不建议这样做.

标签: mysql sorting duplicates constraints unique


【解决方案1】:

考虑到您在此声明中所写的内容:

I did a hackish workaround where ...

我会演奏那句话的前半部分,

sort_order = actual_order + 1000

然后停止(表示你完成了)。

在 100 年内,您永远不会遇到最大值溢出的情况。如果这是令人担忧的,让您的年度活动(通过创建活动)提醒您它离关注有多远,然后将该群体控制回 1+,当它是。

但是您需要更新任何给定的组 2M 次以防溢出。

【讨论】:

  • 这是一个有趣的方法。使用示例 PHP 代码查看我的编辑。尽管这仍然会导致重复,因为实际订单始终以1 开头,例如:1,2,3。也许下次我排序时,我会将其设为负值,然后在下一次排序时,将其设为正值。我不知道,但这可能是对我当前代码的有用增强。谢谢
  • 很乐意通过 PHP 运行它,并在早上或更早的时候帮助解决它。有点喜欢足球比赛:)
【解决方案2】:

当你有如此严格的约束时,你将不得不使用一个中间值:

 UPDATE Table1 SET sort_order = -1 WHERE id = 52 AND group_id = 1;
 UPDATE Table1 SET sort_order =  3 WHERE id = 50 AND group_id = 1;
 UPDATE Table1 SET sort_order =  1 WHERE id = 52 AND group_id = 1;

-1 是中间值。

整个过程:

  • 将记录 1 的给定组的 sort_order 置于中间值;
  • 将记录 2 的 sort_order 设置为所需值;
  • 将记录 1 的 sort_order 设置为所需值;

因此,为了保持约束,您将始终需要更新 2 条记录,1 条记录甚至 2 次。

【讨论】:

    【解决方案3】:

    从外观上看,您的前端正在生成一次更新一行的更新语句。如果在任何时候更新语句导致 2 行具有相同的值,则会导致唯一键冲突。我认为您应该寻找一种方法来在一个语句中使用排序值更新所有行。我不熟悉mysql,但我熟悉sql server。在 sql server 中,我会将表复制到临时表并在那里找出新的排序。然后执行一条更新语句,在一条语句中将新排序值复制回原始表。

    【讨论】:

    • 问题标记为 MySQL。在 MySQL 中,即使是临时的唯一性违规也会失败,因为它是逐行检查而不是语句结束
    【解决方案4】:

    您可以使用 case 语句一次更新所有内容

    UPDATE Table1 set sort_order =
    CASE 
    WHEN id = 50 THEN 3
    WHEN id = 51 THEN 1
    WHEN id = 52 THEN 2
    END
    

    【讨论】:

    • 我用这种类型的语句更新了我的代码,但是mysql仍然认为我违反了密钥。我希望这能奏效......我喜欢有一个单一的声明。
    • 旁注:CASE 表达式,不是声明。 SQL 根本不知道流控制语句。
    • 问题标记为 MySQL。在 MySQL 中,即使是临时的唯一性违规也会失败,因为它是逐行检查而不是语句结束
    猜你喜欢
    • 2020-04-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-16
    • 2017-01-10
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多