问题描述
我使用Postgresql,并且有一个名为table
的表。
该表包含名为created_at
(data_type
为timestamptz
)的列,该列使用BTREE进行了索引。
我想计算一段时间内created_at::date
分组的行数(按from_date
和end_date
过滤)。
我运行以下查询(结果符合预期):
SELECT ("table"."created_at" AT TIME ZONE 'UTC')::date AS "date",COUNT("table"."id") AS "count"
FROM "table"
WHERE ("table"."created_at" >= '2018-08-05T00:00:00+00:00'::timestamptz AND "table"."created_at" <= '2020-09-05T00:00:00+00:00'::timestamptz)
GROUP BY ("table"."created_at" AT TIME ZONE 'UTC')::date
ORDER BY "date" ASC
此查询需要很长时间才能运行(超过200万行),并且在查看查询计划时,我注意到有大量的 Seq扫描:
GroupAggregate (cost=538741.06..605206.42 rows=2954016 width=12) (actual time=3866.460..5077.054 rows=559 loops=1)
Group Key: ((timezone('UTC'::text,created_at))::date)
-> Sort (cost=538741.06..546126.10 rows=2954016 width=8) (actual time=3866.414..4413.922 rows=2954016 loops=1)
Sort Key: ((timezone('UTC'::text,created_at))::date)
Sort Method: external merge disk: 52056kB
-> Seq Scan on table (cost=0.00..140489.32 rows=2954016 width=8) (actual time=0.070..2194.108 rows=2954016 loops=1)
Filter: ((created_at >= '2018-08-05 00:00:00+00'::timestamp with time zone) AND (created_at <= '2020-09-05 00:00:00+00'::timestamp with time zone))
Planning time: 1.018 ms
Execution time: 5094.280 ms
我想了解以下内容:
解决方法
似乎您的WHERE条件实际上并未过滤掉任何行,因此表中的所有行均已处理。在这种情况下,使用Seq扫描是检索数据的最有效方法。如果您将时间范围缩小,以便只检索表的一小部分行,则优化器应使用索引。
Seq扫描仅占用查询时间的一半,另一半用于GROUP BY(或对其进行排序)。如果您增加work_mem
,至少排序/分组应该更快(work_mem
更多),则排序很可能会被散列聚合代替。
假设id
被定义为not null
,那么使用count(*)
代替count(id)
也会使查询速度更快。对于一个,因为在计数功能中不再需要“空检查”。但更重要的是,因为Postgres可能只进行索引扫描,因为只需要在索引中直接使用的created_at
列。如果此操作没有切换为“仅索引扫描”,则可能需要运行vacuum analyze the_table;
来更新可见性图。