PostgreSQL可以在索引中使用空值吗?


10

我读过这本书

数据库假定Indexed_Col IS NOT NULL覆盖的范围太大而无用,因此数据库不会从这种情况驱动到索引。

我认识到这本书已有十多年的历史了,但是它已经被证明是非常有用的-使用从书页中收集到的指令,我将查询速度提高了十倍。

此外,在运行EXPLAIN ANALYZE上一SELECT查询,我发现没有我的指标正在使用,所有的权利,即使他们应该是。

因此,我的问题是:

假设有一个表包含一个列,该列的列定义包括“ NOT NULL”,并且存在一个覆盖该列的索引,那么该索引是否可以用于该表的查询中,该列是查询的一部分?

喜欢:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

Answers:


9

PostgreSQL当然可以为使用索引IS NOT NULL。我也没有看到关于该条件的任何查询计划器假设。

如果列(pg_statistic.stanullfrac)的空分数足够低,以表明该索引对于查询是有用的选择,则PostgreSQL将使用一个索引。

我无法弄清楚您要说的是什么:

如果正确,我是否理解在使用该列的查询中不使用定义为“ NOT NULL”的列上的索引?

当然,索引不会用于列IS NOT NULL上的条件NOT NULL。它总是匹配100%的行,因此seqscan几乎总是快得多。

如果索引没有过滤掉查询的大部分行,PostgreSQL将不会使用索引。唯一可能的例外是,当您要求按单个索引的顺序匹配索引的一组列时。PostgreSQL可能会进行仅索引扫描。例如,如果有索引t(a, b, c)并且您:

select a, b FROM t ORDER BY a, b, c;

PostgreSQL可能会使用您的索引,即使没有过滤掉任何行,因为它只需要读取索引,并且可以跳过读取堆,避免进行排序等。



1
甚至在可为空的列上,带条件的查询WHERE column IS NOT NULL也可能不使用索引,因为如书中所述:“覆盖的范围太大而无用”。如果90%的值不为null,则seqscan也可能会更快。
ypercubeᵀᴹ

究竟。可以,但是只有在表的大部分为空时才可以。无论如何,在这种情况下,部分索引通常是一个更好的选择。
克雷格·林格

是。我试图说的是(据我所知)“覆盖范围太大”部分是指索引,但涉及的是特定条件,而不是一般的索引。
ypercubeᵀᴹ

2
@FuriousFolder嘿,这里有太多的否定。PostgreSQL不会在查询NOT NULL列上使用索引,IS NOT NULL除非该索引也对WHERE子句的其他部分,联接过滤器等有用,或者可用于有序的仅索引扫描。换句话说,它将完全忽略该列IS NOT NULL上的冗余,NOT NULL并根据其他详细信息进行索引使用选择。(请参阅编辑,仅重新索引扫描)。
Craig Ringer 2015年

2

除了Craig的详尽答案之外,我还想补充一点,您所参考的书的封面上写着:

涵盖Oracle,DB2和SQL Server

因此,我不相信它会成为PostgreSQL特别有用的建议。每个RDBMS都可能出奇的不同!

我对您的原始问题有些困惑,但是下面的示例说明了本书的这一部分并非100%正确。为避免进一步的混乱,请参见以下整个段落,您可以在Google图书搜索中查看该段落。

数据库假定Indexed_Col IS NOT NULL覆盖的范围太大而无用,因此数据库不会从这种情况驱动到索引。在极少数情况下,具有任何非空值非常罕见,因此对所有可能的非空值进行索引范围扫描是有益的。在这种情况下,如果您可以找出所有可能值范围的安全下限或上限,则可以使用诸如Positive_ID_Column> -1或Date_Column> TO_DATE('0001/01/01' ,'YYYY / MM / DD')。

实际上,Postgres可以(在以下伪造的情况下)使用索引来满足IS NOT NULL查询,而无需像建议的那样添加范围扫描合并Positive_ID_Column > -1。有关在特定情况下Postgres为什么选择此索引的信息,请参阅有关Craig问题的评论,以及有关使用部分索引的说明。

CREATE TABLE bar (a int);
INSERT INTO bar (a) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO bar (a) VALUES (1);
CREATE INDEX bar_idx ON bar (a);

EXPLAIN ANALYZE SELECT * FROM bar WHERE a IS NOT NULL;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using bar_idx on bar  (cost=0.42..8.44 rows=1 width=4) (actual time=0.094..0.095 rows=1 loops=1)
   Index Cond: (a IS NOT NULL)
   Heap Fetches: 1
 Total runtime: 0.126 ms
(4 rows)

顺便说一下,这是Postgres 9.3,但我相信结果将与9.1大致相似,尽管它不会使用“仅索引扫描”。

编辑:我看到您已经澄清了您的原始问题,并且您显然想知道为什么Postgres在简单的示例中不使用索引:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

可能是因为表中没有任何行。因此,添加一些测试数据和ANALYZE my_table;


在该书的描述中(重点是我的):“作者Dan Tow概述了他开发的一种省时的方法,可以快速,系统地找到最佳执行计划,而不管所使用的SQL或数据库平台的复杂性如何。 ”而且,也许您忽略了问题的#1,即该列定义NOT NULL,而不是该查询IS NOT NULL用作其索引条件。这是在您引用的评论中,但是我将更新问题以使其包含在内。
FuriousFolder

此外,这本书本身与语言无关:只有DMBS专用的部分是关于显示查询计划的,Postgres对此非常简单:)
FuriousFolder 2015年

1
@FuriousFolder列被定义为NOT NULL,但是这一部分(在您的问题中,来自书中):“ Indexed_Col IS NOT NULL涵盖了……”是指where条件,而不是列定义。尽管很难确定,因为它没有上下文。也许您应该包括本书的整个(上一个)段落。
ypercubeᵀᴹ

-1

您尚未发布查询或示例数据。但是不使用索引的最常见原因与卷有关。

索引就像电话簿一样,可以将列转换为行位置。如果仅查找几行,则有必要先查找电话簿中的每一行,然后再查找主表中的行。

但是对于多行而言,跳过电话簿并遍历主表中的所有行要便宜一些。以我的经验,转折点大约是100行。


“索引就像是将一列转换为行位置的电话簿。如果只查找几行,则有必要先查找电话簿中的每一行,然后再查找主表中的行。” 实际上,索引就像较小的电话簿一样,它们在索引的电话簿更新时都会更新。您知道,每次打开较小的电话簿时,都将查找其索引条件描述的所有信息。例如,所有在索引表上名为“ frank”的人:CREATE INDEX ix_frank ON people(name) WHERE name ='frank'
FuriousFolder 2015年

这使得仅索引扫描要 很多更快,因为你可以看到整个“小电话簿”到内存中,这是不符合一个多亿内衬表是可行的。
FuriousFolder 2015年

@FuriousFolder:您正在描述仅索引扫描。但是OP表示没有使用他的索引,如果仅索引扫描可以满足查询,则不会发生。
安多玛(Andomar)

Andomar ...我 OP,哈哈 我的目标就是这样;使此查询使用仅索引扫描。因为我已经实现了它,因为克雷格解释说,Postgres的能够对在列定义包括列使用索引 NOT NULL
FuriousFolder
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.