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()
)似乎违反了这个原则,但事实并非如此。这些函数在内部被重写以使用各自的运算符。
索引表达式必须在运算符的左边。对于大多数运算符(包括上述所有),如果您将索引表达式放在右侧,查询规划器可以通过翻转操作数来实现这一点 - 假设已定义 COMMUTATOR
。 ANY
construct 可以与各种运算符组合使用,它本身不是运算符。当用作constant = ANY (array_expression)
时,只有支持数组元素 上的=
运算符的索引才符合条件,我们需要= ANY()
的交换器。 GIN 索引已失效。
Postgres 目前还不够聪明,无法从中派生 GIN 可索引表达式。首先,constant = ANY (array_expression)
不完全等同于与array_expression @> 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
,不是int2
或int8
)时,没有NULL
值(如您的示例所示)考虑附加模块intarray
,它提供了专门的、更快的运算符和索引支持。见:
至于您的问题中的 UNIQUE
约束没有得到解答:这是通过 整个数组 值上的 btree 索引实现的(就像您怀疑的那样),并且对搜索 元素。详情:
【讨论】:
Aaaaaaaah,现在感觉很尴尬,但我没有想到即使理论上可行,postgres 也不会使用索引。也许这也是因为我对 postgres 缺乏洞察力,例如索引绑定到运算符。感谢您抽出宝贵时间回答我提出的问题并分享您的知识! @Tregoreg:别太尴尬,真的不太明显。我记得当我第一次遇到它时,我自己也被它弄糊涂了。增加的问题和澄清应该对公众非常有用。 根据我的经验,这些索引几乎不提供加速除非gin__int_ops
用于integer[]
列。在我发现这个 op 类之前,我花了多年的挫败感并寻找其他解决方案。这是一个边缘奇迹工作者。
@IamIC:我添加了指向 intarray 的指针。正如您所指出的,似乎值得注意。
对于ANY (array_expression) = constant
表达式,GIN 索引工作正常吗?以上是关于PostgreSQL 可以索引数组列吗?的主要内容,如果未能解决你的问题,请参考以下文章