代码之家  ›  专栏  ›  技术社区  ›  user1032531

使用唯一列更新表

  •  1
  • user1032531  · 技术社区  · 7 年前

    包含以下数据的表正在使用 INNODB ,具有 UNIQUE 约束于 position/fk ,并且不允许 NULL 对于 position

    +----+----------+-----+
    | id | position | fk  |
    +----+----------+-----+
    |  1 |        1 | 123 |
    |  2 |        2 | 123 |
    |  3 |        3 | 123 |
    |  4 |        4 | 123 |
    |  5 |        5 | 123 |
    |  6 |        6 | 123 |
    |  7 |        7 | 123 |
    |  8 |        8 | 123 |
    |  9 |        9 | 123 |
    | 10 |       10 | 123 |
    +----+----------+-----+
    

    PHP收到将表更新为以下内容的请求。可以提供最方便的请求格式,例如 [2,1,4,3,6,5,8,7,10,9] [{"id":1, "position":2}, ... ]

    +----+----------+-----+
    | id | position | fk  |
    +----+----------+-----+
    |  1 |        2 | 123 |
    |  2 |        1 | 123 |
    |  3 |        4 | 123 |
    |  4 |        3 | 123 |
    |  5 |        6 | 123 |
    |  6 |        5 | 123 |
    |  7 |        8 | 123 |
    |  8 |        7 | 123 |
    |  9 |       10 | 123 |
    | 10 |        9 | 123 |
    +----+----------+-----+
    

    我已经确认了 SET unique_checks=0; 不允许临时禁用唯一检查,也不希望实际删除唯一索引、更新表和重新应用唯一索引。

    如何更新此表?

    如果没有简单的方法,我想了几个选择,但不喜欢:

    1. 允许 空值 在里面 位置 。有没有办法暂时允许 空值 类似于如何 SET FOREIGN_KEY_CHECKS=0; 可以禁用外键吗?
    2. 首先删除所有记录,然后重新插入。这可能会导致性能问题,因为表上有需要重新创建的索引。
    1 回复  |  直到 7 年前
        1
  •  2
  •   Bill Karwin    7 年前

    我所能想到的是,首先需要将所有位置更改为一些其他值,这些值不在最终需要设置的新位置值范围内,但在行中仍然是唯一的。

    假设position列是一个有符号整数,一种简单的方法是将所有位置设置为相反的(负值)。它们将保持唯一性,但不在新值集中。

    您可以在事务中以及后续更新中执行此操作,因此任何其他并发事务都不会看到负值。

    BEGIN;
    UPDATE MyTable SET position = -position;
    UPDATE MyTable SET position = 2 WHERE id = 1;
    ...etc... 
    COMMIT;
    

    这是一次黑客攻击。整数的符号位用于显示负数以外的用途。