PostgreSQL可以索引数组列吗?

Iam*_*mIC 132 arrays postgresql indexing

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

我创建了一个包含一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");
Run Code Online (Sandbox Code Playgroud)

索引是否有助于此查询?

Fra*_*ens 161

是的,您可以索引数组,但必须使用数组运算符GIN索引类型.

例:

    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];
Run Code Online (Sandbox Code Playgroud)

结果:

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
Run Code Online (Sandbox Code Playgroud) 注意

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

create index <index_name> on <table_name> using GIN (<column> gin__int_ops)
Run Code Online (Sandbox Code Playgroud)

我还没有看到过它可以在没有gin__int_ops选项的情况下使用&&和@>运算符

  • 正如OP推测的那样,这实际上并不索引单个数组值,而是索引整个数组.因此,虽然这将有助于查询(请参阅解释计划),但这意味着您无法(轻松)在单个数组值上创建唯一约束.也就是说,如果使用整数数组,可以使用contrib模块"intarray"来索引单个数组值,在许多情况下可以快得多.(IIRC在文本价值方面正在做一些工作,但欢迎贡献者帮忙完成它). (18认同)
  • 请不要在代码示例中使用PostgreSQL标识符中的大写字母,这只会让那些不熟悉引用/案例折叠规则的人感到困惑,特别是对PostgreSQL不熟悉的人. (14认同)
  • 在这里重复我的评论:根据我的经验,这些索引提供很少甚至没有加速*除非*`gin__int_ops`用于`integer []`列.在我发现这个操作类之前,我花了很多年的挫折并寻找其他解决方案.这是一个边缘奇迹工作者. (6认同)
  • @IamIC 这是否意味着我不应该费心索引字符串数组?我应该只索引整数数组? (3认同)
  • 仅当您安装了“intarray”扩展时才需要运算符类“gin__int_ops”,否则索引默认工作。我在这里对此进行了扩展:/sf/ask/4479751811/#63996455 (3认同)

Erw*_*ter 84

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

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

@ Frank接受的答案告诉你使用数组运算符,这对于Postgres 11 来说仍然正确的.手册:

... PostgreSQL的标准发行版包括一个数组的GIN运算符类,它支持使用这些运算符的索引查询:

<@
@>
=
&&
Run Code Online (Sandbox Code Playgroud)

此处列出了标准分发中GIN索引的内置运算符类的完整列表.

在Postgres中,索引绑定到运算符(为某些类型实现),而不是单独的数据类型或函数或其他任何类型.这是伯克利原设计Postgres遗产,现在很难改变.它通常工作得很好.这是一个关于pgsql-bugs的线程,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 int2int8)时NULL(如您的示例所示)考虑附加模块intarray,它提供专门的,更快的运算符和索引支持.看到:

至于UNIQUE你的问题中没有答案的约束:这是用整个数组值的btree索引实现的(就像你怀疑的那样)并且根本没有帮助搜索元素.细节:

  • @Tregoreg:不要太尴尬,这真的不太明显.我记得当我第一次遇到它时,我自己也很困惑.增加的问题和澄清应该对公众非常有用. (6认同)
  • @IamIC:我添加了指向intarray的指针。正如您指出的那样,似乎值得关注。 (2认同)

Ed4*_*Ed4 36

现在可以索引各个数组元素.例如:

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)
Run Code Online (Sandbox Code Playgroud)

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

  • 让它不会丢失 - 对于您想要使用ANY()运算符的可变长度数组,这种方法是没有希望的. (26认同)
  • 这真的不是很有用.如果你有一个固定数量的数组元素,你宁可为每个元素(和普通的btree索引)使用单独的列,而不是为每个数组项构建一个更昂贵的表达式索引.单个列的存储也更便宜,没有阵列开销. (21认同)