如何使简单的 GROUP BY 使用索引?

How to make simple GROUP BY use index?

我想通过温度计的温度读数获取给定 table 的每小时平均温度,行结构:thermometer_id, timestamp (float, julian days), value (float) 加上 timestamp 上的升序索引。

为了得到 4 天前的一整天,我正在使用这个查询:

SELECT 
    ROUND(AVG(value), 2), -- average temperature
    COUNT(*)              -- count of readings
FROM reads
WHERE 
    timestamp >= (julianday(date('now')) - 5) -- between 5 days
    AND 
    timestamp < (julianday(date('now')) - 4)  -- ...and 4 days ago
GROUP BY CAST(timestamp * 24  as int)         -- make hours from floats, group by hours

效果不错,但速度很慢,对于一个 9MB 的数据库,355k 行,它需要超过半秒才能完成,这是令人困惑的长,应该不会超过几十秒小姐。它在不太快的硬件(虽然不是 ssd)上这样做,但我正准备在 raspberry pi 上使用它,相比之下相当慢 + 它每天的工作将增加 80k 行。

Explain解释原因:

"USE TEMP B-TREE FOR GROUP BY"

我已经尝试添加 dayhour 列的索引只是为了快速访问,但是,group by 仍然没有使用任何索引。

如何调整此查询或数据库以加快此查询速度?

如果使用索引来优化 GROUP BY,则无法再优化 timestamp 搜索(除非使用 skip-scan optimization,您的旧 SQLite 可能没有)。并且遍历 reads 中的 所有 行,只是因为时间戳不匹配而将其中的大部分丢弃,效率不高。

如果 SQLite 没有自动执行正确的操作,即使在 运行 ANALYZE 之后,您可以尝试强制它使用特定索引:

CREATE INDEX rhv ON reads(hour, value);
SELECT ... FROM reads INDEXED BY rhv WHERE timestamp ... GROUP BY hour;

但这不太可能产生实际上更快的查询计划。

正如@colonel-thirty-two 评论的那样,问题出在 GROUP BY CAST(timestamp * 24 as int) 上的转换和乘法。这样的分组将完全忽略索引,因此查询时间很慢。当我使用 hour 列进行时间比较和分组时,查询立即完成。