为啥子查询解决方法中的这个 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 后面不能传变量的问题怎么解决
Mysql5.7中子查询时order by与group by合用无效的解决办法
MySQL 如何处理查询中的 ORDER BY 和 LIMIT?