在嵌套 SQL 查询中排序

Posted

技术标签:

【中文标题】在嵌套 SQL 查询中排序【英文标题】:Order by in nested SQL query 【发布时间】:2020-05-04 19:28:56 【问题描述】:

关于我在 mysql 中使用“order by”指令时遇到的一个奇怪事实,我希望得到您的帮助

让我们看看下表:

CREATE TABLE `test_nested_order_by` (
  `id` int(11) NOT NULL,
  `timestamp` int(11) NOT NULL COMMENT 'Timestamp',
  `index_continuity_month` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

insert into test_nested_order_by (id,timestamp,index_continuity_month) values (1,1583772141,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (1,1583708400,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (5,1583708400,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (4,1583708400,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (5,1583794800,0) ;
insert into test_nested_order_by (id,timestamp,index_continuity_month) values (4,1583794800,0) ;

如您所见,每行的“index_continuity_month”列都设置了 0 值。

我现在想按如下方式设置这个值:一个唯一的值,它将按 id 和 timestamp 列的升序递增。该表看起来像:

mysql>  select * from test_nested_order_by :

+----+------------+------------------------+
| id | timestamp  | index_continuity_month |
+----+------------+------------------------+
|  1 | 1583772141 |                      2 |
|  1 | 1583708400 |                      1 |
|  5 | 1583708400 |                      5 |
|  4 | 1583708400 |                      3 |
|  5 | 1583794800 |                      6 |
|  4 | 1583794800 |                      4 |
+----+------------+------------------------+
6 rows in set (0,00 sec)

或者,如果您愿意:

mysql> select * from test_nested_order_by order by id,timestamp ;
+----+------------+------------------------+
| id | timestamp  | index_continuity_month |
+----+------------+------------------------+
|  1 | 1583708400 |                      1 |
|  1 | 1583772141 |                      2 |
|  4 | 1583708400 |                      3 |
|  4 | 1583794800 |                      4 |
|  5 | 1583708400 |                      5 |
|  5 | 1583794800 |                      6 |
+----+------------+------------------------+

为此,我使用以下查询:

UPDATE  test_nested_order_by t1,
(SELECT
id,
timestamp,
@last_continuity_month := @last_continuity_month +1, @last_continuity_month AS index_continuity_month

FROM test_nested_order_by, (

SELECT @last_continuity_month :=0
)SQLVars
ORDER BY id , timestamp) t2

SET t1.index_continuity_month = t2.index_continuity_month

WHERE t1.id = t2.id
      AND t1.timestamp = t2.timestamp;

但是当我看到结果时,它似乎不起作用:

mysql> select * from test_nested_order_by order by id,timestamp ;
+----+------------+------------------------+
| id | timestamp  | index_continuity_month |
+----+------------+------------------------+
|  1 | 1583708400 |                      2 |
|  1 | 1583772141 |                      1 |
|  4 | 1583708400 |                      4 |
|  4 | 1583794800 |                      6 |
|  5 | 1583708400 |                      3 |
|  5 | 1583794800 |                      5 |
+----+------------+------------------------+
6 rows in set (0,00 sec)

我怀疑没有考虑“order by”指令(如果我从查询中删除它,结果完全一样)。

我们可以注意到index_continuity_month的递增并不是按照id和timestamp列的升序进行的,而是按照行在表中插入的顺序进行的。

但是,如果我只运行查询的嵌套部分:

SELECT
id,
timestamp,
@last_continuity_month := @last_continuity_month +1, @last_continuity_month AS index_continuity_month

FROM test_nested_order_by, (

SELECT @last_continuity_month :=0
)SQLVars
ORDER BY id , timestamp;

+----+------------+-----------------------------------------------------+------------------------+
| id | timestamp  | @last_continuity_month := @last_continuity_month +1 | index_continuity_month |
+----+------------+-----------------------------------------------------+------------------------+
|  1 | 1583708400 |                                                   1 |                      1 |
|  1 | 1583772141 |                                                   2 |                      2 |
|  4 | 1583708400 |                                                   3 |                      3 |
|  4 | 1583794800 |                                                   4 |                      4 |
|  5 | 1583708400 |                                                   5 |                      5 |
|  5 | 1583794800 |                                                   6 |                      6 |
+----+------------+-----------------------------------------------------+------------------------+

效果不错!

有没有人可以解释我的问题是什么?更具体地说,为什么 SQL 查询在嵌套到另一个查询时没有相同的行为?

非常感谢!

【问题讨论】:

MyISAM ?为什么?你的主键是什么? 你运行的是哪个版本的 MySQL? 主键是 id 和时间戳,但为了简单起见,我从示例中删除了它,因为它不会改变任何问题 我的 MySQL 版本是 5.7 【参考方案1】:

作为初学者:如果您运行的是 MySQL 8.0,则使用 row_number() 非常简单:

update test_nested_order_by t
inner join (
    select 
        t.*, 
        row_number() over(order by id, timestamp) rn 
    from test_nested_order_by t
) t1 on t1.id = t.id and t1.timestamp = t.timestamp
set t.index_continuity_month  = t1.rn 

在早期版本中,用户变量确实是一种解决方案;但是将它们与order by 一起使用非常棘手。那是因为order by 通常在select 子句之后处理,因此不能保证将“正确”值分配给每一行。要解决此问题,您需要先在子查询中对表进行排序,然后设置变量:

update test_nested_order_by t
inner join (
    select t.*, @rn := @rn + 1 rn
    from (select * from test_nested_order_by order by id, timestamp) t
    cross join (select @rn := 0) x
) t1 on t1.id = t.id and t1.timestamp = t.timestamp
set t.index_continuity_month  = t1.rn 

Demo on DB Fiddle - 两个update 查询都会产生以下结果:

select * from test_nested_order_by order by id, timestamp
编号 |时间戳 | index_continuity_month -: | ---------: | ---------------------: 1 | 1583708400 | 1 1 | 1583772141 | 2 4 | 1583708400 | 3 4 | 1583794800 | 4 5 | 1583708400 | 5 5 | 1583794800 | 6

【讨论】:

非常感谢。只是一个额外的问题:slaakso 已经说过(我在这里读过:mariadb.com/kb/en/why-is-order-by-in-a-from-subquery-ignored)在子查询中可能会忽略“order by”指令。您认为它会损害您向我指出的解决方案(第二个)吗?还是不行?【参考方案2】:

子查询结果是一组无序的行。因此 MySQL 优化器可以忽略子查询中的ORDER BY

【讨论】:

我完全不知道 能否请您给我指定的文档? 行为来自 SQL 标准。 MySQL 只是遵循标准。早期的 MySQL 版本(5.5?)在子查询中确实遵循 ORDER BY。大多数 SQL 数据库甚至不允许在子查询中使用 ORDER BY。如果您还使用 LIMIT(并且限制小于行数),MySQL 确实遵循 ORDER。 好的,谢谢,我发现:mariadb.com/kb/en/why-is-order-by-in-a-from-subquery-ignored;我想它与你的意思有关

以上是关于在嵌套 SQL 查询中排序的主要内容,如果未能解决你的问题,请参考以下文章

过滤和排序 SQL 查询以重新创建嵌套结构

MySQL 嵌套连接排序

在sql中对不相关嵌套查询的处理原则

带嵌套的Sql语句执行顺序问题

SQL数据查询之——嵌套查询

嵌套 MySQL 查询和字母数字排序