PostgreSQL:为什么这个查询没有使用我的索引?

时间:2014-01-22 06:37:39

标签: sql postgresql indexing

我正在对数据库执行此查询(所有数字和列名称都已组成):

select * from t where a=1 and b=11 and c!=5 and d<8

t有一个索引:

create index i on t (a,b,c,d)

当我运行“EXPLAIN ANALYZE”时,查询执行顺序扫描,大约需要55ms才能执行此操作。如果我像这样修改查询:

select * from t where a=1 and b=11 and c=5 and d<8
                                       ^

它使用索引并在0.5毫秒内完成。所以它必须是NOT EQUALS,对吗?不是这样,因为如果我这样做了查询:

select * from t where a=1 and b=11 and c=5 and d!=8
                                               ^

查询仍然使用索引。但如果我试试这个,没有索引:

select * from t where a=1 and b=11 and c<5 and d<8
                                       ^

那么为什么Postgres表现得如此?这对我来说很奇怪。

2 个答案:

答案 0 :(得分:0)

我会说它没有使用第一个查询的索引,因为索引并没有真正帮助,因为几乎整个表都匹配。在这种情况下,整个表的扫描速度更快。最后两个查询之间的差异在于索引可能仅在预期结果大小低于某个阈值时使用。具有完全匹配的查询很可能产生的结果少于使用少于仍然产生少于不等于选择的结果。

话虽如此,查询优化器是一个非常复杂的软件,通常会产生令人惊讶的结果。

答案 1 :(得分:0)

正如您已经意识到的那样,问题与使用equals以外的运算符有关。索引只能最有效地用于与equals(加上一个范围条件)进行比较的最左侧列。

在你的例子中:

create index i on t (a,b,c,d);
where a=1 and b=11 and c!=5 and d<8;

它只能有效地使用ab的索引。这意味着数据库将获取与ab条件匹配的所有行,然后根据剩余条件检查每一行。

当您将c上的过滤器更改为等于时,它会(可能)获取更少的行(仅限匹配abc的行),然后检查这些行(更少)行与d过滤器相对应。在这种情况下,使用索引会更有效。

通常,PostgreSQL查询规划器会评估两个选项:(1)使用索引; (2)做一个SeqScan。对于两者,它计算成本值 - 越高,预期性能越差。因此,它需要具有较小成本价值的那个。这是它决定使用索引的方式,没有固定的阈值。

最后,上面写了“加一个范围条件”。这意味着,如果您使用等号,那么它不仅可以以最有效的方式使用索引,而且还可以用于单一范围条件。

考虑到您的查询中有一个单一范围条件,我建议更改索引,如下所示:

create index i on t (a,b,d,c);

现在,它可以有效地使用ab以及d上的过滤器与索引,并且只需要过滤c!=5所在的行。尽管此索引可以作为原始查询更有效地用于查询,但它并不自动意味着PG将使用它。这取决于成本估算。但试一试。

最后,如果这不是快速,您在表达式5中使用的值c!=5是常量,您可能会考虑部分索引:

 create index i on t (a,b,d)
        where c!=5;

如果您比较它们的值是常量,那么您也可以对所有其他列执行此操作。

参考文献: