为什么PostgreSQL没有使用索引" WHERE NOT IN"条件。

时间:2016-09-08 05:27:30

标签: postgresql

我有两个表db100和db60具有相同的字段:x,y,z。 为字段z上的两个表创建索引,如下所示:

CREATE INDEX db100_z_idx
  ON db100
  USING btree
  (z COLLATE pg_catalog."default");
CREATE INDEX db60_z_idx
  ON db60
  USING btree
  (z COLLATE pg_catalog."default");

尝试在db100中找到db60中不存在的z值:

select db60.z from db60 where db60.z not in (select db100.z from db100)

据我了解,执行查询所需的所有信息都显示在索引中。所以,我希望只使用索引。 但是,它使用表格上的顺序扫描:

"Seq Scan on db60  (cost=0.00..25951290012.84 rows=291282 width=4)"
"  Filter: (NOT (SubPlan 1))"
"  SubPlan 1"
"    ->  Materialize  (cost=0.00..80786.26 rows=3322884 width=4)"
"          ->  Seq Scan on db100  (cost=0.00..51190.84 rows=3322884 width=4)"

有人可以解释为什么PostgreSQL在这个例子中没有使用索引吗?

这两个表都包含几百万个记录,执行需要一段时间。

我知道使用"的左连接是空的"条件给出更好的结果。但问题是这个特定的语法。

我是PG v 9.5

1 个答案:

答案 0 :(得分:2)

SubPlan 1适用于select db100.z from db100。您选择 所有 行,因此索引无用。你真的想select DISTINCT z from db100在这里,然后应该使用索引。

在主查询中,您有select db60.z from db60 where db60.z not in ...。同样,您选择 所有 行,除非条件 为真,因此索引不再适用,因为它适用于逆条件。

通常,仅当规划器认为此类使用会加快查询处理时才使用索引。它总是取决于有多少不同的值以及行如何在磁盘上的物理页面上分布。搜索具有特定值的列的所有行的索引与查找 的行具有相同值的行不同;索引指示在哪些页面以及在哪些位置查找行,但该集合不能简单地反转。

鉴于 - 在您的情况下 - z是某种text类型,有意义的"否定"无法构造索引(这实际上几乎是一个真实的主义,尽管在某些情况下可以设想一个"否定的索引)。您应该查看trigram indexes,因为这些文本索引的工作速度比btree快得多。

您真的想要提取具有相同z值的所有291,282行,或者也可以在此处使用DISTINCT子句?这应该可以加快速度。