为啥子查询解决方法中的这个 ORDER BY 不能始终如一地工作?

Posted

技术标签:

【中文标题】为啥子查询解决方法中的这个 ORDER BY 不能始终如一地工作?【英文标题】:Why does this ORDER BY in sub-query workaround not work consistently?为什么子查询解决方法中的这个 ORDER BY 不能始终如一地工作? 【发布时间】:2020-12-29 22:56:28 【问题描述】:

为了获取某个标识符组合的最新记录,我使用以下查询:

SELECT t1.*
FROM (
    SELECT id, b_id, c_id
    FROM a
    ORDER BY epoch DESC
    LIMIT 18446744073709551615
) AS t1
GROUP BY t1.b_id, t1.c_id

如果b_id + c_id 的组合有多个记录,那么它总是会选择具有最高值epoch 的记录(因此,最晚的时间)。

LIMIT 添加为解决方法to force MariaDB to actually order the results。我在我的应用程序中成功地大量使用了这种结构,others 也是如此。

但是,现在我在我的应用程序中遇到了完全相同的查询,我“不小心”使用了比子查询中严格必要的列更多的列:

SELECT t1.*
FROM (
    SELECT id, b_id, c_id, and, some, other, columns, ...
    FROM a
    ORDER BY epoch DESC
    LIMIT 18446744073709551615
) AS t1
GROUP BY t1.b_id, t1.c_id

我已经测试了这两个查询。完全相同的查询,但只更改那些额外的列,会使结果变得不正确。实际上,列数决定了结果。如果我有 a 的总数)。在我的测试中,删除或添加哪一列似乎并不重要。

我很难找出为什么在我添加更多列后行为会发生变化。另外,也许是偶然的,它昨天仍然给出了正确的结果。但是今天突然结果发生了变化,可能是在将新记录(具有不相关的标识符)添加到表a 之后。我试过使用EXPLAIN:

# The first query, with columns: id, b_id, c_id
 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    280     Using where; Using temporary; Using filesort
2   DERIVED     a   ALL     NULL    NULL    NULL    NULL    280     Using filesort

# The second query, with columns: id, b_id, c_id, and, some, other, columns, ...
 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    276     Using where; Using temporary; Using filesort
2   DERIVED     a   ALL     NULL    NULL    NULL    NULL    276     Using filesort

但这并没有真正帮助我,除了我可以看到key_len 是不同的。在第二个查询中错误接收的第二个最新记录是 id = 276,它使用第一个查询正确检索的实际最新记录是 id = 278。现在总共有 307 行,而昨天可能只有约 300 行。我不确定如何解释这些结果以了解出了什么问题。有人知道吗?如果没有,我还能做些什么来找出导致这些奇怪结果的原因?

【问题讨论】:

请通过包含示例输入和输出数据来使这个问题成为一个完整的问题。 【参考方案1】:

为什么不使用窗口函数而不是这种肮脏的变通方法,它依赖于 mysql/MariaDB 关于group by 的非标准行为?

select *
from (
    select a.*, row_number() over(partition by b_id, c_id order by epoch desc) rn
    from a
) a
where rn = 1

这适用于 MySQL 8.0 和 Maria DB 10.2 或更高版本。在早期版本中,一种替代方法是相关子查询:

select *
from a
where epoch = (select max(a1.epoch) from a a1 where a1.b_id = a.b_id and a1.c_id = a.c_id)

【讨论】:

【参考方案2】:

这是一个格式错误的查询,应该会产生语法错误:

SELECT t1.*
FROM (SELECT id, b_id, c_id
      FROM a
      ORDER BY epoch DESC
      LIMIT 18446744073709551615
     ) t1
GROUP BY t1.b_id, t1.c_id;

为什么?您正在选择没有聚合函数的 3 列。但是group by 只有两列。幸运的是,这现在是 MySQL 中的语法错误,使用默认设置。最后! (MySQL 在 8.0 版之前接受了这种非标准语法。)

您可以使用相关子查询做您想做的事:

select a.*
from a
where a.epoch = (select max(a2.epoch)
                 from a a2
                 where a2.b_id = a.b_id and a2.c_id = a.c_id
                );

使用a(b_id, c_id, epoch) 上的索引,这可能也比聚合更快——即使这在某些情况下碰巧起作用。

【讨论】:

我必须选择比分组依据更多的列,因为我还想获得id 列。我需要在分组前订购,我不明白这应该是一件坏事吗?您能否确认它是格式错误/语法错误的原因是因为根据 SQL 定义,子查询的结果是无序的,正如我所读到的那样?但是,您对使用相关子查询的建议可能是一个合适的选择,谢谢。 @Yeti 。 . .阅读答案。使用相关子查询。您不能在 group by 查询中包含与键不一致的未聚合列。时期。如果您一直在使用该构造,那么您一直在错误地使用 SQL,并且您正确地使用了它。

以上是关于为啥子查询解决方法中的这个 ORDER BY 不能始终如一地工作?的主要内容,如果未能解决你的问题,请参考以下文章

SQL 分页查询存储过程中order by 后面不能传变量的问题怎么解决

为啥子查询会在 group by 查询中抛出错误?

Mysql5.7中子查询时order by与group by合用无效的解决办法

MySQL 如何处理查询中的 ORDER BY 和 LIMIT?

如何应用:大查询中的count(distinct ...)超过(partition by ... order by)?

sql语句在查询分析器中能执行,但是程序却不能执行,一直报错order by 附近有语法错误