优化查询以获取整行,其中一个字段是组的最大值

Posted

技术标签:

【中文标题】优化查询以获取整行,其中一个字段是组的最大值【英文标题】:Optimizing query to get entire row where one field is the maximum for a group 【发布时间】:2019-02-24 02:59:40 【问题描述】:

我有一个表,其架构类似于,例如,

EventTime   DATETIME(6),
EventType   VARCHAR(20),
Number1     INT,
Number2     INT,
Number3     INT,
...

在这个表中有难以想象的大量行,但是为了这个查询,我只感兴趣,比如说,在EventTime 的两个给定值之间的几千行。 EventTime 上有一个索引,如果我只是做类似的事情

SELECT * FROM table WHERE EventTime >= time1 and EventTime <= time2;

然后它能够​​几乎立即返回相关行。

在这个时间窗口的行中,我想精确地提取那些Number1 是具有EventType 的任何行中最大的行。所以换句话说,我想做一些与这个查询等效的事情:

SELECT * FROM
  (SELECT EventType, MAX(Number1) as max_Number1
   FROM table
   WHERE EventTime >= time1 AND EventTime <= time2
   GROUP BY EventType) AS a
  LEFT JOIN
  (SELECT * FROM table
   WHERE EventTime >= time1 AND EventTime <= time2) AS b
  ON a.EventType = b.EventType AND a.max_Number1 = b.Number1)

这看起来应该可以正常工作——我可以运行每个子查询,即

SELECT EventType, MAX(Number1) as max_Number1
FROM table
WHERE EventTime >= time1 AND EventTime <= time2
GROUP BY EventType;

SELECT * FROM table
WHERE EventTime >= time1 AND EventTime <= time2;

几乎是即时的,因此此时产生所需结果应该不会太难:数据库可以按EventType 对两个子查询的结果进行排序或索引,然后进行匹配。

但是,当我实际运行它时,它需要 永远。我不知道要多久,因为我从来没有让它完成,但它比我手动提取两个查询的结果并在其他地方进行合并所需的时间更长。

问题:

    为什么需要这么长时间?数据库引擎在做什么? 有没有办法以合理的方式编写这个查询? 如果不是,我可以将它写成存储过程吗?

困难:由于该表有数百亿行,因此向其添加任何进一步的索引将非常昂贵。

【问题讨论】:

【参考方案1】:

实际上,您已经非常接近一个好的查询。您的主要缺点可能是在时间范围内从table 中选择所有内容时的 LEFT JOIN。请尝试以下操作:

SELECT * FROM
table b
INNER JOIN (
    SELECT EventType, MAX(Number1) as max_Number1
    FROM table
    WHERE EventTime >= time1 AND EventTime <= time2
    GROUP BY EventType
) AS a
ON a.EventType = b.EventType
AND a.max_Number1 = b.Number1
WHERE b.EventTime >= time1 AND b.EventTime <= time2

理想情况下,这将伴随一个索引(EventType,EventTime)。请在您的问题中提供SHOW CREATE TABLE table,以便我们查看您目前拥有的索引。我们或许可以调整现有索引,或帮助您删除不需要的索引,以允许添加此新索引。

免责声明:我的经验仅适用于 mysql 和 InnoDB,但我认为这对 MariaDB 和 MyISAM 仍然有帮助。

【讨论】:

有机会我会试试这个——目前正在从我的手机中恢复。你能谈谈为什么左连接是一个问题,为什么这应该是一个改进? 左连接不是问题本身,而是您编写它是一个(SELECT ... ) 连接这一事实,它往往优化不佳,因为它往往会导致临时表被保存这些中间结果,并且没有任何索引可用于该临时表。 好的,所以我一到电脑上就尝试了这个,它在大约半秒内运行。就可以了! 您对书籍、讲座等有什么建议吗?可以让我了解此类内容? 手册是很好的入门资源。这是描述我用于此问题的查询结构的页面:dev.mysql.com/doc/refman/8.0/en/… 了解索引也有很大帮助。如果有人问你“索引有什么作用?”并且您回答“它加快了查询”而不是“它制作了数据的副本,使得在复制的列上搜索更容易”然后花一些时间研究索引。我不知道您在做什么,也不知道,所以请多阅读。这里的 [query-optimization] 标签有很多有用的答案。

以上是关于优化查询以获取整行,其中一个字段是组的最大值的主要内容,如果未能解决你的问题,请参考以下文章

查询以获取每个人的最大捐款

oracle 查询每组的最大值

获取每个组的最大字段的整个记录

春季启动查询以单独从子文档数组中的字段中获取最大值

子查询中匹配条件的每个组的最大值

获取每个月 SQL 的组的 MAX 值