为SELECT DISTINCT ON创建的索引无效?为何仍执行顺序扫描?
SCD Type2查询未使用索引的问题解答
问题背景
我创建了如下player表:
CREATE TABLE public.player ( company_id character varying NOT NULL, id character varying NOT NULL, created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at timestamp with time zone, name character varying NOT NULL, family character varying, start_from timestamp with time zone DEFAULT now() NOT NULL, surrogate_key character varying NOT NULL );
为实现缓慢变化维度(SCD)Type 2,我执行了以下查询以获取每个surrogate_key的最新记录:
SELECT DISTINCT ON ( surrogate_key ) surrogate_key, * FROM player ORDER BY surrogate_key, created_at DESC;
查询功能正常,但执行计划显示采用顺序扫描(Seq Scan):
Unique (cost=10.47..10.99 rows=101 width=171) (actual time=0.098..0.115 rows=101 loops=1) -> Sort (cost=10.47..10.73 rows=103 width=171) (actual time=0.097..0.100 rows=103 loops=1) Sort Key: surrogate_key, created_at DESC Sort Method: quicksort Memory: 46kB -> Seq Scan on player (cost=0.00..7.03 rows=103 width=171) (actual time=0.013..0.039 rows=103 loops=1) Planning Time: 0.076 ms Execution Time: 0.135 ms
我创建了匹配查询的索引:
CREATE INDEX ON player (surrogate_key, created_at DESC);
但执行计划仍显示顺序扫描,想知道:这是否正常?索引创建是否错误?“创建索引就不会用顺序扫描”的想法是否有误?
解答
这是正常现象,你的索引创建完全正确,优化器选择顺序扫描的核心原因是当前表数据量太小(仅103行)。
PostgreSQL的查询优化器会基于成本选择执行计划:对于极小的数据集,顺序扫描不需要额外的索引IO和查找开销,直接全表读取后排序的成本反而比走索引更低——你当前的执行时间仅0.135ms,已经是最优状态了。
你的索引(surrogate_key, created_at DESC)完美匹配查询需求:
- 满足
DISTINCT ON (surrogate_key)的分组逻辑 - 完全对齐
ORDER BY surrogate_key, created_at DESC的排序顺序
当表数据量增长到一定规模(比如数万行),优化器会自动切换为索引扫描,此时可以跳过全表排序,直接通过索引有序读取每个surrogate_key的第一条(最新)记录,性能会显著提升。
若要验证索引有效性,可临时禁用顺序扫描强制测试:
SET enable_seqscan = off; SELECT DISTINCT ON ( surrogate_key ) surrogate_key, * FROM player ORDER BY surrogate_key, created_at DESC;
查看此时的执行计划,会看到索引扫描(Index Scan)的逻辑,证明索引可用。
内容的提问来源于stack exchange,提问作者Fred Hors
相关产品推荐
相关产品推荐

