自动重新排序行并更新排序列的值

Automatically re-sort rows and update the values of a sort column

我有一个带有自动递增索引的 MariaDB table,还有一个“sortorder”字段,用于控制查询和显示数据时的排序顺序。

例如

id    title     sortorder
1     this      10
2     that      30
3     other     20
4     something 25

到目前为止,还不错。我想创建一个函数来自动重新排序这些 - 好吧,不是重新排序,而是根据现有订单重做 sortorder 列的值。在 运行 函数之后,上面的期望结果是这样的:

id    title     sortorder
1     this      10
2     that      40
3     other     20
4     something 30

这是否可以通过 MariaDB 中的 SQL 语句来完成(我还没有找到任何相关的东西),或者我是否需要在我的 (php) 应用程序中完成它?

新排序顺序值的逻辑基于排序顺序列的排序。

重新编号的原因是排序顺序值将在应用程序中手动维护,但从头开始可能偶尔会有所帮助。用户将接受培训,在值中“留出一些空间”,以便将来进行编辑。

在第一天,“sortorder”将(手动)填充 10、20、30 等,或者可能是 100、200、300 等。这样,如果他们需要重新排序将来,这将允许将一个项目的排序顺序值更改为 25,将其放在 具有 20 和 30 的项目之间。有意义吗?

但最终,用户可能会把自己逼到墙角,或者至少让事情让自己感到困惑。最好为它们构建一个简单地遍历行并重新设置所有排序顺序值的按钮,以保留现有的行顺序,但使排序顺序的值重新按间隔均匀分布。

更新:此解决方案适用于 MySQL 8.0,但不适用于 MariaDB,因为 MariaDB 对 CTE 的支持不支持 UPDATE 语句。我将把这个解决方案留给使用 MySQL 的读者,但它不适用于 MariaDB。

mysql> select * from NoOneEverNamesTheirTableInSqlQuestions;
+----+-----------+-----------+
| id | title     | sortorder |
+----+-----------+-----------+
|  1 | this      |        10 |
|  2 | that      |        30 |
|  3 | other     |        20 |
|  4 | something |        25 |
+----+-----------+-----------+

mysql> with cte as (
    select id, row_number() over (order by sortorder) * 10 as new_sortorder 
    from NoOneEverNamesTheirTableInSqlQuestions
  ) 
  update NoOneEverNamesTheirTableInSqlQuestions join cte using (id) 
  set sortorder = new_sortorder;
Query OK, 2 rows affected (0.01 sec)
Rows matched: 4  Changed: 2  Warnings: 0

mysql> select * from NoOneEverNamesTheirTableInSqlQuestions;
+----+-----------+-----------+
| id | title     | sortorder |
+----+-----------+-----------+
|  1 | this      |        10 |
|  2 | that      |        40 |
|  3 | other     |        20 |
|  4 | something |        30 |
+----+-----------+-----------+

这就需要在里面写一些子查询。我做的步骤如下:

  • Table 我用的名字是tt。您需要根据您的 table 姓名进行更改。
  • 首先是按 sortorder 列的排序顺序获取所有行。
  • 其次,声明一个变量,比如 @serial_no 并在每个 selected 行上将其递增 1。这是一种老派技术,但我发现它更具可读性。
  • 在这个新的父 select 查询中分配新的 sortorder 值。现在,我只是将它的序列号(如等级)乘以 10。您可以相应地进行调整。
  • 在您的更新查询中,内部连接正在使用此 select 查询更新的 table 的当前副本,并通过在 [=17] 上匹配它们来正确更新新的 sortorder 列值=]列。

片段:

update tt A 
inner join (
    select id, title, (@serial_no := @serial_no + 1) as serial_no,@serial_no * 10 as `sortorder`
    from (
        select *
        from tt
        order by sortorder asc
    ) temp_derived,(SELECT @serial_no := 0) as sn    
) B
on A.id = B.id
set A.sortorder = B.sortorder

更新:

我刚刚意识到控制权已完全从用户转移到数据库。如果您希望使用新的 sortorder 值更新多行,我想提出一种解决方法,因为我从未见过使用用户批量提交的新值更新多行(很高兴了解是否存在)。

  • 您需要将旧值映射到新值,比如在 PHP 中的关联数组中。
  • 在 MySQL 中开始一个数据库事务。
  • 批量插入所有新行。
  • 使用从 PHP 发送的 ID 一次性删除所有以前的旧行(在准备好的语句中最好使用之前映射的 assoc 数组)
  • 提交交易。
  • 如果出现问题,回滚课程。

在 MariaDB 中你可以做:

update t
join (
  select id, 10 * row_number() over (order by sortorder) as rn10
  from t
) x on x.id = t.id
set t.sortorder = x.rn10;

结果:

 id  title      sortorder 
 --- ---------- --------- 
 1   this       10        
 2   that       40        
 3   other      20        
 4   something  30        

请参阅 db<>fiddle 中的 运行 示例。