顺序更新mysql表

use*_*531 1 mysql sql sequential

给定下表,在删除一行或多行后,如何position使用单个查询按顺序从 1 到 N 重新排序,并且仍然保留 的顺序position

+---------+----------+-----+
| id (pk) | position | fk  |
+---------+----------+-----+
|       4 |        1 | 123 |
|       2 |        2 | 123 |
|      18 |        3 | 123 |
|       5 |        4 | 123 |
|       3 |        5 | 123 |
+---------+----------+-----+
Run Code Online (Sandbox Code Playgroud)

例如,如果position=1 ( id=4) 被删除,则所需的最终记录为:

+---------+----------+-----+
| id (pk) | position | fk  |
+---------+----------+-----+
|       2 |        1 | 123 |
|      18 |        2 | 123 |
|       5 |        3 | 123 |
|       3 |        4 | 123 |
+---------+----------+-----+
Run Code Online (Sandbox Code Playgroud)

如果position=3(id=18)被删除,则所需的最终记录为:

+---------+----------+-----+
| id (pk) | position | fk  |
+---------+----------+-----+
|       4 |        1 | 123 |
|       2 |        2 | 123 |
|       5 |        3 | 123 |
|       3 |        4 | 123 |
+---------+----------+-----+
Run Code Online (Sandbox Code Playgroud)

如果仅删除行而不删除多行,我可以执行类似以下操作。

DELETE FROM mytable WHERE fk=123 AND position = 4;
UPDATE mytable SET position=position-1 WHERE fk=123 AND position > 4;
Run Code Online (Sandbox Code Playgroud)

fan*_*nts 5

如果您还没有使用 MySQL 8,则用户定义的变量可以提供帮助,MySQL 8 提供了 ROW_NUMBER() 等窗口函数:

UPDATE t
JOIN (

    SELECT 
    t.*
    , @n := @n + 1 as n
    FROM t
    , (SELECT @n := 0) var_init
    ORDER BY position

) sq ON t.id = sq.id
SET t.position = sq.n;
Run Code Online (Sandbox Code Playgroud)

奖金:

当您有多个组时,情况会变得稍微复杂一些。
例如,对于这样的样本数据

|  id | position |  fk |
|-----|----------|-----|
|   4 |        1 | 123 |
|   2 |        2 | 123 |
|   5 |        4 | 123 |
|   3 |        5 | 123 |
|  40 |        1 | 234 |
|  20 |        2 | 234 |
| 180 |        3 | 234 |
|  30 |        5 | 234 |
Run Code Online (Sandbox Code Playgroud)

查询将是

UPDATE t
JOIN (

    SELECT 
    t.*
    , @n := if(@prev_fk != fk, 1, @n + 1) as n
    , @prev_fk := fk
    FROM t
    , (SELECT @n := 0, @prev_fk := NULL) var_init
    ORDER BY fk, position

) sq ON t.id = sq.id
SET t.position = sq.n;
Run Code Online (Sandbox Code Playgroud)

这里你只需将当前的 fk 保存在另一个变量中。当处理下一行时,变量仍然保存“前一行”的值。@n然后,当值发生变化时,您可以重置变量。

更新:

row_number()在 MySQL 8 中,您可以像这样使用窗口函数:

update t join (
    select t.*, row_number() over (partition by fk order by position) as new_pos 
    from t
) sq using (id) set t.position = sq.new_pos;
Run Code Online (Sandbox Code Playgroud)

  • 实际上`(SELECT @n := 0) var_init`是一个`CROSS JOIN`,您可以在其中初始化变量.. + 1作为答案确实是使用MySQL用户变量的好方法。 (2认同)