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

时间:2015-12-02 22:31:01

标签: performance sqlite group-by query-performance

我希望获得温度读数的给定表的每小时平均温度,行结构: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),但我准备在树莓派上使用它,相比之下相当慢,它每天可以获得8万多行。

Explain解释原因:

“使用TEMP B-TREE for GROUP BY”

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

如何调整此查询或数据库以使此查询更快?

2 个答案:

答案 0 :(得分:1)

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

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

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

但这不太可能导致查询计划实际上更快。

答案 1 :(得分:0)

正如@ Colonel-three-two评论的那样,问题在于=IFERROR(INDEX($8:$8,MATCH(A13,$5:$5,0)),"") 上的强制转换和乘法。这种分组将完全省略索引,因此查询时间较慢。当我使用GROUP BY CAST(timestamp * 24 as int)列进行时间比较和分组时,查询立即完成。