我在以下查询中遇到了Postgres优化器的奇怪行为:
select count(product0_.id) as col_0_0_ from Product product0_
where product0_.active=true
and (product0_.aggregatorId is null
or product0_.aggregatorId in ($1 , $2 , $3))
Product
有大约54列,active
是一个有btree索引的布尔值,aggregatorId
是'varchar(15)`并且有一个btree索引。
在上面的查询中,未使用'aggregatorId'的索引:
Aggregate (cost=169995.75..169995.76 rows=1 width=32) (actual time=3904.726..3904.727 rows=1 loops=1)
-> Seq Scan on product product0_ (cost=0.00..165510.39 rows=1794146 width=32) (actual time=0.055..2407.195 rows=1851827 loops=1)
Filter: (active AND ((aggregatorid IS NULL) OR ((aggregatorid)::text = ANY ('{5109037,5001015,70601}'::text[]))))
Rows Removed by Filter: 542146
Total runtime: 3904.925 ms
但是如果我们通过省略对该列的空检查来减少查询,则使用索引:
Aggregate (cost=17600.93..17600.94 rows=1 width=32) (actual time=614.933..614.935 rows=1 loops=1)
-> Index Scan using idx_prod_aggr on product product0_ (cost=0.43..17487.56 rows=45347 width=32) (actual time=19.284..594.509 rows=12099 loops=1)
Index Cond: ((aggregatorid)::text = ANY ('{5109037,5001015,70601}'::text[]))
Filter: active
Rows Removed by Filter: 49130
Total runtime: 150.255 ms
据我所知,btree索引可以处理空检查,所以我不明白为什么索引不用于完整的查询。产品表包含大约230万个条目,因此速度不是很快。
编辑: 该指数非常标准:
CREATE INDEX idx_prod_aggr
ON product
USING btree
(aggregatorid COLLATE pg_catalog."default");
答案 0 :(得分:1)
你的问题看起来很有趣,所以我再现了你的场景 - postgres 9.1,包含1M行的表,一个布尔列,一个varchar列,两个索引,一半表都有NULL名称。
当varchar列不已编入索引时,我有相同的解释分析输出。但是,索引postgres在NULL条件和IN条件下使用位图扫描,然后将它们与OR条件合并。
然后他在布尔条件下使用seq扫描(因为索引是分开的)
explain analyze
select * from A where active is true and ((name is null) OR (name in ('1','2','3') ));
见输出:
"Bitmap Heap Scan on a (cost=17.34..21.35 rows=1 width=18) (actual time=0.048..0.048 rows=0 loops=1)"
" Recheck Cond: ((name IS NULL) OR ((name)::text = ANY ('{1,2,3}'::text[])))"
" Filter: (active IS TRUE)"
" -> BitmapOr (cost=17.34..17.34 rows=1 width=0) (actual time=0.047..0.047 rows=0 loops=1)"
" -> Bitmap Index Scan on idx_prod_aggr (cost=0.00..4.41 rows=1 width=0) (actual time=0.010..0.010 rows=0 loops=1)"
" Index Cond: (name IS NULL)"
" -> Bitmap Index Scan on idx_prod_aggr (cost=0.00..12.93 rows=1 width=0) (actual time=0.036..0.036 rows=0 loops=1)"
" Index Cond: ((name)::text = ANY ('{1,2,3}'::text[]))"
"Total runtime: 0.077 ms"
这让我觉得您错过了一些细节,如果是这样,请将它们添加到您的问题中。
答案 1 :(得分:1)
由于您在where子句中使用的列有许多相同的值(根据您的数字,所有表行的78%),数据库将得出结论,使用全表扫描比浪费额外的更便宜是时候阅读索引了。
大多数数据库供应商的经验法则是,如果无法将搜索范围缩小到所有表记录的约5%,则可能不会使用该索引。