【发布时间】:2021-02-07 11:55:50
【问题描述】:
在对多个列执行GROUP BY 操作后在 PostgreSQL(11、12、13)的列上选择 MIN 时,不会使用在分组列上创建的任何索引:https://dbfiddle.uk/?rdbms=postgres_13&fiddle=30e0f341940f4c1fa6013677643a0baf
CREATE TABLE tags (id serial, series int, index int, page int);
CREATE INDEX ON tags (page, series, index);
INSERT INTO tags (series, index, page)
SELECT
ceil(random() * 10),
ceil(random() * 100),
ceil(random() * 1000)
FROM generate_series(1, 100000);
EXPLAIN ANALYZE
SELECT tags.page, tags.series, MIN(tags.index)
FROM tags GROUP BY tags.page, tags.series;
HashAggregate (cost=2291.00..2391.00 rows=10000 width=12) (actual time=108.968..133.153 rows=9999 loops=1)
Group Key: page, series
Batches: 1 Memory Usage: 1425kB
-> Seq Scan on tags (cost=0.00..1541.00 rows=100000 width=12) (actual time=0.015..55.240 rows=100000 loops=1)
Planning Time: 0.257 ms
Execution Time: 133.771 ms
理论上,索引应该允许数据库以(tags.page, tags.series) 的步长进行查找,而不是执行全盘扫描。这将导致上述数据集的处理行数为 10,000,而不是 100,000。 This link 描述了没有分组列的方法。
This answer(以及this one)建议使用带有排序的DISTINCT ON 而不是GROUP BY,但这会产生这个查询计划:
Unique (cost=0.42..5680.42 rows=10000 width=12) (actual time=0.066..268.038 rows=9999 loops=1)
-> Index Only Scan using tags_page_series_index_idx on tags (cost=0.42..5180.42 rows=100000 width=12) (actual time=0.064..227.219 rows=100000 loops=1)
Heap Fetches: 100000
Planning Time: 0.426 ms
Execution Time: 268.712 ms
虽然现在正在使用索引,但它似乎仍在扫描完整的行集。使用 SET enable_seqscan=OFF 时,GROUP BY 查询会降级为相同的行为。
如何鼓励 PostgreSQL 使用多列索引?
【问题讨论】:
标签: postgresql indexing group-by query-optimization aggregate-functions