PostgreSQL可以索引数组列吗?


144

我在文档中找不到这个问题的明确答案。如果列是数组类型,是否将对所有输入的值分别进行索引?

我创建了一个只有一int[]列的简单表,并在其上添加了唯一索引。我注意到我无法添加相同的int数组,这使我相信索引是数组项的组合,而不是每个项的索引。

INSERT INTO "Test"."Test" VALUES ('{10, 15, 20}');
INSERT INTO "Test"."Test" VALUES ('{10, 20, 30}');

SELECT * FROM "Test"."Test" WHERE 20 = ANY ("Column1");

索引对这个查询有帮助吗?


Answers:


180

是的,您可以为数组建立索引,但是必须使用数组运算符GIN-index类型

例:

    CREATE TABLE "Test"("Column1" int[]);
    INSERT INTO "Test" VALUES ('{10, 15, 20}');
    INSERT INTO "Test" VALUES ('{10, 20, 30}');

    CREATE INDEX idx_test on "Test" USING GIN ("Column1");

    -- To enforce index usage because we have only 2 records for this test... 
    SET enable_seqscan TO off;

    EXPLAIN ANALYZE
    SELECT * FROM "Test" WHERE "Column1" @> ARRAY[20];

结果:

Bitmap Heap Scan on "Test"  (cost=4.26..8.27 rows=1 width=32) (actual time=0.014..0.015 rows=2 loops=1)
  Recheck Cond: ("Column1" @> '{20}'::integer[])
  ->  Bitmap Index Scan on idx_test  (cost=0.00..4.26 rows=1 width=0) (actual time=0.009..0.009 rows=2 loops=1)
        Index Cond: ("Column1" @> '{20}'::integer[])
Total runtime: 0.062 ms
注意

在许多情况下,似乎需要gin__int_ops选项

create index <index_name> on <table_name> using GIN (<column> gin__int_ops)

我还没有看到没有gin__int_ops选项就可以与&&和@>运算符一起使用的情况


19
正如OP推测的那样,这实际上并不索引单个数组的值,而是索引整个数组。因此,尽管这将有助于所讨论的查询(请参阅说明计划),但这意味着您不能(轻松)在单个数组值上创建唯一约束。就是说,如果您使用整数数组,则可以使用contrib模块“ intarray”来索引各个数组的值,这在许多情况下会更快。(IIRC正在进行一些有关文本值的工作,但是可能欢迎贡献者帮助完成它)。
xzilla 2011年

15
请不要在代码示例中的PostgreSQL标识符中使用大写字母,这只会使不熟悉引号/大小写折叠规则的人(特别是PostgreSQL初学者)感到困惑。
intgr

6
在这里重复我的评论:根据我的经验,除非 gin__int_ops用于integer[]列,否则这些索引几乎无法提供加速。我花了多年的挫败感并寻找其他解决方案,直到我发现了这个运算类。这是一个临界的奇迹工作者。
IamIC

1
@IamIC表示我不应该麻烦索引字符串数组吗?而且我应该只索引整数数组?
ryan2johnson9

93

@Tregoreg 在对他提供的赏金的评论中提出了一个问题

我没有找到当前的答案。在数组类型的列上使用GIN索引不会提高ANY()运算符的性能。真的没有解决办法吗?

@弗兰克接受的答案告诉你使用数组运算符,这仍然是正确的Postgres的11 本手册:

... PostgreSQL的标准发行版包括用于数组的GIN运算符类,它支持使用以下运算符进行索引查询:

<@
@>
=
&&

这里是标准分发版中GIN索引的内置运算符类的完整列表。

在Postgres中,索引绑定到运算符(为某些类型实现),而不是单独的数据类型或函数或其他任何东西。这是Postgres最初Berkeley设计的传承,现在很难更改。而且通常工作正常。这是pgsql-bug上的一个线程,Tom Lane对此进行了评论。

一些PostGis 函数(如ST_DWithin())似乎违反了该原则,但事实并非如此。这些函数在内部重写以使用相应的运算符

索引表达式必须在运算符的左侧。对于大多数运算符(包括上述所有运算符),如果COMMUTATOR已将索引表达式放在右侧(假定已定义a),则查询计划器可以通过翻转操作数来实现此目的。该ANY构造可以与各种运算符结合使用,而不是运算符本身。当constant = ANY (array_expression)仅用作对数组元素=上的运算符提供支持的索引时,我们将需要使用一个换向器。GIN索引出了。= ANY()

Postgres目前还不够智能,无法从中获取GIN可索引的表达式。对于初学者来说,constant = ANY (array_expression)不完全等同array_expression @> ARRAY[constant]。如果涉及任何NULL 元素,则数组运算符将返回错误,而该ANY构造可以在任一侧处理NULL。数据类型不匹配的结果也不同。

相关答案:

阿西德斯

使用没有值的integer数组int4,not int2int8NULL(如您的示例所示)时,请考虑附加模块intarray,它提供了专门的,更快的运算符和索引支持。看到:

至于UNIQUE您的问题中尚未解决的约束:它是通过对整个数组值使用btree索引来实现的(就像您所怀疑的那样),并且根本无法帮助查找元素。细节:


1
Aaaaaaah,现在感到很尴尬,但是我没想到即使理论上可行,postgres也不会使用该索引。也许是因为我缺乏对postgres的洞察力,例如索引绑定到运算符。感谢您抽出宝贵时间回答我的问题,并分享您的知识!
Tregoreg 2015年

6
@Tregoreg:别太尴尬,这真的不是太明显。我记得自己第一次遇到它时就被它弄糊涂了。所增加的问题和澄清对公众应该是非常有用的。
Erwin Brandstetter

1
根据我的经验,除非 gin__int_ops用于integer[]列,否则这些索引几乎无法提供加速。我花了多年的挫败感并寻找其他解决方案,直到我发现了这个运算类。这是一个临界的奇迹工作者。
IamIC

2
@IamIC:我添加了指向intarray的指针。正如您指出的那样,似乎值得关注。
Erwin Brandstetter

对于ANY (array_expression) = constant表达式,GIN索引可以正常工作吗?
user10375

37

现在可以索引单个数组元素。例如:

CREATE TABLE test (foo int[]);
INSERT INTO test VALUES ('{1,2,3}');
INSERT INTO test VALUES ('{4,5,6}');
CREATE INDEX test_index on test ((foo[1]));
SET enable_seqscan TO off;

EXPLAIN ANALYZE SELECT * from test WHERE foo[1]=1;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using test_index on test  (cost=0.00..8.27 rows=1 width=32) (actual   time=0.070..0.071 rows=1 loops=1)
   Index Cond: (foo[1] = 1)
 Total runtime: 0.112 ms
(3 rows)

这至少适用于Postgres 9.2.1。请注意,您需要为每个数组索引构建一个单独的索引,在我的示例中,我仅索引了第一个元素。


28
让它不会丢失-对于要使用ANY()运算符的可变长度数组,这种方法是没有希望的。
Καrτhικ

24
这确实不是很有用。如果您有固定数量的数组元素,则宁愿为每个元素使用单独的列(以及纯btree索引),而不是为每个数组项构建更昂贵的表达式索引。没有阵列开销,单个列的存储也便宜得多。
Erwin Brandstetter 2015年
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.