在 PostgreSQL 9.3 中为 json 字段的嵌套属性创建索引的多种方法

Posted

技术标签:

【中文标题】在 PostgreSQL 9.3 中为 json 字段的嵌套属性创建索引的多种方法【英文标题】:Multiple ways to create index on a json field's nested property in PostgreSQL 9.3 【发布时间】:2014-06-09 18:29:37 【问题描述】:

在 PostgreSQL 9.3 中,有多种方式来构建一个表达式,它指向一个 json 字段的嵌套属性:

data->'foo'->>'bar'
data#>>'foo,bar'
json_extract_path_text(data, 'foo', 'bar')

因此,如果查询的表达式与索引的表达式完全匹配,PostgreSQL 只会使用这些索引。

CREATE TABLE json_test_index1(data json);
CREATE TABLE json_test_index2(data json);
CREATE TABLE json_test_index3(data json);

CREATE INDEX ON json_test_index1((data->'foo'->>'bar'));
CREATE INDEX ON json_test_index2((data#>>'foo,bar'));
CREATE INDEX ON json_test_index3((json_extract_path_text(data, 'foo', 'bar')));

-- these queries use an index, while all other combinations not:

EXPLAIN SELECT * FROM json_test_index1 WHERE data->'foo'->>'bar' = 'baz';
EXPLAIN SELECT * FROM json_test_index2 WHERE data#>>'foo,bar' = 'baz';
EXPLAIN SELECT * FROM json_test_index3 WHERE json_extract_path_text(data, 'foo', 'bar') = 'baz';

我的问题是:

这种行为是有意的吗?我认为查询优化器应该(至少)将索引与#>> 运算符一起使用,当查询包含对json_extract_path_text() 的适当调用时——反之亦然。

如果我想在我的应用程序中使用更多这些表达式(不仅仅是一个,f.ex。坚持使用->->> 运算符),我应该构建哪些索引? (我希望,不是全部。)

有没有机会,一些未来的 Postgres 版本的优化器会理解这些表达式的等价性?

编辑

当我为此创建一个额外的运算符时:

CREATE OPERATOR ===> (
    PROCEDURE = json_extract_path_text,
    LEFTARG = json,
    RIGHTARG = text[]
);

这个查询(上例中的表)仍然没有使用它的索引:

EXPLAIN SELECT * FROM json_test_index3 WHERE data ===> 'foo,bar' = 'baz';

额外问题:

虽然 Postgres 将运算符扩展为函数调用(在幕后),但为什么仍然不使用它的索引?

【问题讨论】:

对于当前版本 (9.3),这是“有意的”(即,它只是没有实现)。 9.4 将包括对 JSON 内部存储方式的重大改进,使其能够用于 GIN 索引。然后#>> 运算符应该能够使用索引(类似于当前索引hstore 数据类型时的功能)。如果您有需要索引的“扁平”键/值对,目前您唯一的选择是hstore(或等待 9.4) @a_horse_with_no_name 虽然jsonb 字段上的 GIN 索引将提供更灵活的索引,但它会带来性能成本。我只需要查询 json 对象内的 few 属性,并且特定属性自己的索引 应该 更快。我只是想知道,如果有多种方法可以获得一个属性,为什么我需要为所有这些方法建立一个索引,以利用一个(在每种情况下)。还是我错过了什么? 不,你并没有错过什么。仅当查询包含与索引中使用的表达式相同的表达式时,才使用基于函数的索引。关于成本论点:我认为 jsonb 列上的单个 GIN 索引的成本可能会小于只能由单个表达式使用的多个索引(每个属性一个)的组合成本(一个完全用于财产)。但目前我没有看到如何避免这种情况的方法(前提是你必须坚持使用 JSON)。 @a_horse_with_no_name 几个索引的综合成本是一个公平的观点。我可以等待 GIN 索引。 【参考方案1】:

您必须对 JSON 和 JSONB 数据类型使用 GIN 索引。 您可以为计划的查询使用运算符参数 示例:

CREATE INDEX idx_tbl_example ON tbl_example USING GIN(your_jsonb_field);

如果你打算只使用@>操作符,你可以使用jsonb_path_ops参数

CREATE INDEX idx_tbl_example ON tbl_example USING GIN(your_jsonb_field jsonb_path_ops);

其他选择是documented on postgresql site

我想你可以用这个:

CREATE INDEX idx_tbl_example ON tbl_example USING GIN(your_jsonb_field json_extract_path_text);

【讨论】:

以上是关于在 PostgreSQL 9.3 中为 json 字段的嵌套属性创建索引的多种方法的主要内容,如果未能解决你的问题,请参考以下文章

在 Entity Framework Core 中为 PostgreSQL 表的计算列设置公式

PostgreSQL 8.4 和 9.3

如何在CentOS 7/6.5/6.4 下安装PostgreSQL 9.3 与 phpPgAdmin

将 Excel 数据导入 PostgreSQL 9.3

Ubuntu 16.04 安装 postgresql 9.3

如何在CentOS 7/6.5/6.4 下安装PostgreSQL 9.3 与 phpPgAdmin