优化查询以获取整行,其中一个字段是一组的最大值
Optimizing query to get entire row where one field is the maximum for a group
我有一个 table 的架构,比如
EventTime DATETIME(6),
EventType VARCHAR(20),
Number1 INT,
Number2 INT,
Number3 INT,
...
这个 table 中有难以想象的大量行,但是为了这个查询,我只对其中的几千行感兴趣,这些行介于两个给定值 EventTime
。 EventTime
上有一个索引,如果我只是做类似
的事情
SELECT * FROM table WHERE EventTime >= time1 and EventTime <= time2;
然后它能够return 近乎即时的相关行。
这次 window 的行中,我想精确提取那些 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
对两个子查询的结果进行排序或索引,然后将它们匹配起来。
然而,当我实际上 运行 这需要 永远 。我不知道多长时间,因为我从来没有让它完成,但它比我手动提取两个查询的结果并在别处进行合并花费的时间更长。
问题:
- 为什么要花这么长时间?数据库引擎在做什么?
- 有没有办法以合理执行的方式编写此查询?
- 如果没有,我能不能把它写成存储过程?
难度:由于此 table 有数百亿行,因此向其添加任何进一步的索引将非常昂贵。
您实际上已经非常接近一个好的查询了。你的主要缺点可能是 LEFT JOIN 在时间范围内从 table
中选择全部。尝试以下操作:
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 应该仍然有帮助。
我有一个 table 的架构,比如
EventTime DATETIME(6),
EventType VARCHAR(20),
Number1 INT,
Number2 INT,
Number3 INT,
...
这个 table 中有难以想象的大量行,但是为了这个查询,我只对其中的几千行感兴趣,这些行介于两个给定值 EventTime
。 EventTime
上有一个索引,如果我只是做类似
SELECT * FROM table WHERE EventTime >= time1 and EventTime <= time2;
然后它能够return 近乎即时的相关行。
这次 window 的行中,我想精确提取那些 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
对两个子查询的结果进行排序或索引,然后将它们匹配起来。
然而,当我实际上 运行 这需要 永远 。我不知道多长时间,因为我从来没有让它完成,但它比我手动提取两个查询的结果并在别处进行合并花费的时间更长。
问题:
- 为什么要花这么长时间?数据库引擎在做什么?
- 有没有办法以合理执行的方式编写此查询?
- 如果没有,我能不能把它写成存储过程?
难度:由于此 table 有数百亿行,因此向其添加任何进一步的索引将非常昂贵。
您实际上已经非常接近一个好的查询了。你的主要缺点可能是 LEFT JOIN 在时间范围内从 table
中选择全部。尝试以下操作:
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 应该仍然有帮助。