根据排名列对所有行进行排序

时间:2020-11-08 17:30:07

标签: mysql sorting

我有以下sortable表:

| id | txt | position |
|----|-----|----------|
| 1  | aaa | 1        |
| 2  | bbb | 2        |
| 3  | ccc | 3        |

现在,我想更改头寸顺序。假设我想将第3行移至位置1,同时保持其他行的顺序不变:

| id | txt | position |
|----|-----|----------|
| 1  | aaa | 2        |
| 2  | bbb | 3        |
| 3  | ccc | 1        |

在将要放在最上面的行更新到位置0之后,可以通过此查询来实现此目的:

SET @row_number:=0;
UPDATE 
sortable,
(
    SELECT 
    @row_number:=ifnull(@row_number, 0)+1 AS new_position,
    id 
    FROM sortable
    ORDER BY position
) AS table_position
SET position=table_position.new_position
WHERE table_position.id=sortable.id;

将行移动到第一个位置时,此方法效果很好。但是在尝试将行移动到第二个(或其他任何位置)时会遇到麻烦。

我需要一些帮助修复查询的方法,这样我就可以将任何行移动到任何位置,并且它将相应地更新其他行。因此,如果我将3移至2,则2变为3。如果我将1移至3,则2变为1,而3变为2。希望您能理解。显然,现实世界中的数据将有更多的行。这只是一个例子。

2 个答案:

答案 0 :(得分:1)

请考虑以下内容...

DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table
(txt VARCHAR(12) PRIMARY KEY
,position INT NOT NULL
);

INSERT INTO my_table VALUES
('aaa',1),
('bbb',2),
('ccc',3),
('ddd',4),
('eee',5),
('fff',6);

假设我们要将文本从第4位移到第1位...

SELECT *
            , CASE WHEN position < 4 THEN position +1 
                   WHEN position = 4 THEN 1
                   ELSE position END new_pos
         FROM my_table
         
   +-----+----------+---------+
   | txt | position | new_pos |
   +-----+----------+---------+
   | aaa |        1 |       2 |
   | bbb |        2 |       3 |
   | ccc |        3 |       4 |
   | ddd |        4 |       1 |
   | eee |        5 |       5 |
   | fff |        6 |       6 |
   +-----+----------+---------+

...我们可以将其重写为UPDATE ...

UPDATE my_table x
  JOIN 
     ( SELECT *
            , CASE WHEN position < 4 THEN position +1 
                   WHEN position = 4 THEN 1
                   ELSE position END new_pos
         FROM my_table
     ) y
    ON y.txt = x.txt
   SET x.position = y.new_pos;
   
SELECT * FROM my_table;
   +-----+----------+
   | txt | position |
   +-----+----------+
   | aaa |        2 |
   | bbb |        3 |
   | ccc |        4 |
   | ddd |        1 |
   | eee |        5 |
   | fff |        6 |
   +-----+----------+
      

答案 1 :(得分:1)

类似的事情应该起作用:

SET @old_number:=3;
SET @new_number:=5;

UPDATE my_table x
JOIN
(
    SELECT *
    , CASE WHEN @old_number > @new_number THEN
                CASE WHEN position = @old_number THEN @new_number
                     WHEN position >= @new_number AND position < @old_number THEN position +1
                     ELSE position END 
           WHEN @old_number < @new_number THEN
                CASE WHEN position = @old_number THEN @new_number
                     WHEN position <= @new_number AND position > @old_number THEN position -1
                     ELSE position END 
           ELSE position END new_position
    FROM my_table
    ORDER BY position
) y
ON y.position = x.position
SET x.position = y.new_position;

如果您预先进行了一些设置变量的准备工作,这会变得更加简洁:

SET @old_position:=1;
SET @new_position:=5;

SET @low_position = LEAST(@old_position, @new_position);
SET @high_position = GREATEST(@old_position, @new_position);
SET @position_modifier = CASE WHEN @old_position > @new_position THEN 1 ELSE -1 END;

UPDATE my_table x
JOIN
(
    SELECT *
    , CASE WHEN position = @old_position THEN @new_position
           WHEN position BETWEEN @low_position AND @high_position THEN position + @position_modifier
           ELSE position 
      END new_position
    FROM my_table
    ORDER BY position
) y
ON y.position = x.position
SET x.position = y.new_position;

在后一版本中,子查询的WHEN语句中CASE子句的顺序很重要。

基本上,位置需要相应地调整:

  • 当旧职位编号大于新职位编号时,旧职位和新职位之间的所有职位(不包括旧职位)都需要增加。

  • 当旧职位编号小于新职位编号时,旧职位与新职位之间的所有职位(不包括旧职位)都需要递减。

注意BETWEEN包含范围,这就是WHEN子句的顺序很重要的原因。您希望当前位置与旧位置编号的匹配首先发生,以免通过WHEN测试而落入BETWEEN