PostgreSQL 可以索引数组列吗?

Posted

技术标签:

【中文标题】PostgreSQL 可以索引数组列吗?【英文标题】:Can PostgreSQL index array columns? 【发布时间】:2011-05-02 19:18:56 【问题描述】:

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

我创建了一个带有 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");

索引是否有助于此查询?

【问题讨论】:

是否可以使用数据类型jsonb 并使用索引? postgresql.org/docs/9.5/static/functions-json.html 和 postgresql.org/docs/9.5/static/datatype-json.html#JSON-INDEXING 【参考方案1】:

是的,您可以索引一个数组,但您必须使用 array operators 和 GIN-index type。

例子:

    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 选项的情况下它可以与 && 和 @> 运算符一起使用的情况

【讨论】:

正如 OP 推测的那样,这实际上并不索引单个数组值,而是索引整个数组。因此,虽然这将有助于相关查询(请参阅解释计划),但这意味着您不能(轻松)对单个数组值创建唯一约束。也就是说,如果您使用整数数组,则可以使用 contrib 模块“intarray”来索引单个数组值,这在许多情况下会快得多。 (IIRC 正在为文本值做一些工作,但可能会欢迎贡献者帮助完成它)。 请不要在代码示例中的 PostgreSQL 标识符中使用大写字母,这只会让不熟悉引号/大小写折叠规则的人感到困惑,尤其是对 PostgreSQL 不熟悉的人。 在这里重复我的评论:根据我的经验,这些索引几乎没有提供加速除非gin__int_ops 用于integer[] 列。在我发现这个 op 类之前,我花了多年的挫败感并寻找其他解决方案。这是一个边缘奇迹工作者。 @IamIC 这是否意味着我不应该费心索引字符串数组?而且我应该只索引整数数组? Operator class "gin__int_ops" 只有在你安装了 "intarray" 扩展时才需要,否则索引默认工作。我在这里对此进行了扩展:***.com/questions/63996454/…【参考方案2】:

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

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。请注意,您需要为每个数组索引建立一个单独的索引,在我的示例中,我只索引了第一个元素。

【讨论】:

让它不要丢失 - 这种方法对于要使用 ANY() 运算符的可变长度数组是没有希望的。 这真的不是很有用。如果您有固定数量的数组元素,您宁愿为每个元素使用单独的列(以及普通的 btree 索引),而不是为每个数组项构建更昂贵的表达式索引。如果没有阵列开销,存储单个列的成本要低得多。【参考方案3】:

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

我没有发现当前的答案有效。使用 GIN 索引 数组类型的列不会提高 ANY() 的性能 操作员。真的没有办法吗?

@Frank's accepted answer 告诉您使用 数组运算符,对于 Postgres 11,仍然正确。The manual:

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

<@
@>
=
&&

The complete list of built-in operator classes for GIN indexes in the standard distribution is here.

在 Postgres 中,索引绑定到运算符(针对某些类型实现),而不是单独的数据类型或函数或其他任何东西。那是heritage from the original Berkeley design of Postgres,现在很难改变。它通常工作得很好。 Here is a thread on pgsql-bugs with Tom Lane commenting on this.

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

索引表达式必须在运算符的左边。对于大多数运算符(包括上述所有),如果您将索引表达式放在右侧,查询规划器可以通过翻转操作数来实现这一点 - 假设已定义 COMMUTATORANY construct 可以与各种运算符组合使用,它本身不是运算符。当用作constant = ANY (array_expression) 时,只有支持数组元素 上的= 运算符的索引才符合条件,我们需要= ANY() 的交换器。 GIN 索引已失效。

Postgres 目前还不够聪明,无法从中派生 GIN 可索引表达式。首先,constant = ANY (array_expression)不完全等同于array_expression @&gt; ARRAY[constant]。如果涉及任何 NULL 元素,则数组运算符会返回错误,而 ANY 构造可以处理任一侧的 NULL。并且数据类型不匹配也会有不同的结果。

相关答案:

Check if value exists in Postgres array

Index for finding an element in a JSON array

SQLAlchemy: how to filter on PgArray column types?

Can IS DISTINCT FROM be combined with ANY or ALL somehow?

旁白

在使用 integer 数组int4,不是int2int8)时,没有NULL 值(如您的示例所示)考虑附加模块intarray,它提供了专门的、更快的运算符和索引支持。见:

How to create an index for elements of an array in PostgreSQL? Compare arrays for equality, ignoring order of elements

至于您的问题中的 UNIQUE 约束没有得到解答:这是通过 整个数组 值上的 btree 索引实现的(就像您怀疑的那样),并且对搜索 元素。详情:

How does PostgreSQL enforce the UNIQUE constraint / what type of index does it use?

【讨论】:

Aaaaaaaah,现在感觉很尴尬,但我没有想到即使理论上可行,postgres 也不会使用索引。也许这也是因为我对 postgres 缺乏洞察力,例如索引绑定到运算符。感谢您抽出宝贵时间回答我提出的问题并分享您的知识! @Tregoreg:别太尴尬,真的不太明显。我记得当我第一次遇到它时,我自己也被它弄糊涂了。增加的问题和澄清应该对公众非常有用。 根据我的经验,这些索引几乎不提供加速除非gin__int_ops 用于integer[] 列。在我发现这个 op 类之前,我花了多年的挫败感并寻找其他解决方案。这是一个边缘奇迹工作者。 @IamIC:我添加了指向 intarray 的指针。正如您所指出的,似乎值得注意。 对于ANY (array_expression) = constant 表达式,GIN 索引工作正常吗?

以上是关于PostgreSQL 可以索引数组列吗?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在 PostgreSQL 中索引数组列的位置?

我应该在 Oracle 中索引主键列吗

索引postgresql数组列以进行大于/小于比较?

PostgreSQL 多列索引,包括数组

MySQL建表语句转PostgreSQL建表语句全纪录

为啥我的 PostgreSQL 数组索引没有被使用(Rails 4)?