Postgres - 这是在布尔列上创建部分索引的正确方法吗?

时间:2011-12-15 03:57:00

标签: sql postgresql postgresql-9.1

我有下表:

CREATE TABLE recipemetadata
(
  --Lots of columns
  diet_glutenfree boolean NOT NULL,
);

大多数每一行都会被设置为FALSE,除非有人想出了一些疯狂的新无麸质饮食,这种饮食可以扫除这个国家。

我需要能够非常快速地查询此值为true的行。我创建了索引:

CREATE INDEX IDX_RecipeMetadata_GlutenFree ON RecipeMetadata(diet_glutenfree) WHERE diet_glutenfree;

它似乎工作,但我无法弄清楚如何确定它是否只是索引值为true的行。我想确保它没有像使用任何值索引任何行一样愚蠢。

我应该在WHERE子句中添加运算符,还是这种语法完全有效?希望这不是那些超级简单的RTFM问题之一,将会被投票30次。

更新

我已经使用随机值向RecipeMetadata添加了10,000行。然后我在桌子上做了一个ANALYZE和一个REINDEX来确定。当我运行查询时:

select recipeid from RecipeMetadata where diet_glutenfree;

我明白了:

'Seq Scan on recipemetadata  (cost=0.00..214.26 rows=5010 width=16)'
'  Filter: diet_glutenfree'

因此,即使只有大约一半的行具有此标志,它似乎也在对表执行顺序扫描。索引被忽略了。

如果我这样做:

select recipeid from RecipeMetadata where not diet_glutenfree;

我明白了:

'Seq Scan on recipemetadata  (cost=0.00..214.26 rows=5016 width=16)'
'  Filter: (NOT diet_glutenfree)'

所以无论如何,这个索引都没有被使用。

2 个答案:

答案 0 :(得分:4)

我已经确认索引按预期工作。

我重新创建了随机数据,但这次只将diet_glutenfree设置为random() > 0.9,因此on位的概率只有10%。

然后我重新创建索引并再次尝试查询。

SELECT RecipeId from RecipeMetadata where diet_glutenfree;

返回:

'Index Scan using idx_recipemetadata_glutenfree on recipemetadata  (cost=0.00..135.15 rows=1030 width=16)'
'  Index Cond: (diet_glutenfree = true)'

SELECT RecipeId from RecipeMetadata where NOT diet_glutenfree;

返回:

'Seq Scan on recipemetadata  (cost=0.00..214.26 rows=8996 width=16)'
'  Filter: (NOT diet_glutenfree)'

似乎我的第一次尝试受到了污染,因为PG估计扫描整个表格会更快,而不是如果它必须加载超过一半的行而点击索引。

但是,我想我会在列的完整索引上得到这些确切的结果。有没有办法验证在部分索引中索引的行数?

<强>更新

指数约为40k。我创建了同一列的完整索引,它超过200k,所以看起来它肯定是偏的。

答案 1 :(得分:1)

一位字段的索引没有意义。为了理解规划者做出的决定,您必须考虑页面而不是行。

对于8K页面和80个(存储的)行大小,每页有100行。假设随机分布,页面仅包含true值的行的可能性是可忽略的,pow (0.5, 100),大约1e-33,IICC。 (对于&#39; false&#39;当然也是如此)因此,对于gluten_free == true上的查询,每个页面都必须被提取,然后进行过滤。使用索引只会导致更多页面(:索引)被提取。