针对多个值的 PostgreSql JSONB SELECT

jgm*_*jgm 7 postgresql json postgresql-9.4

我有一个非常简单的 JSON 表,其中填充了一些示例数据:

CREATE TABLE jsonthings(d JSONB NOT NULL);

INSERT INTO jsonthings VALUES ('{"name":"First","tags":["foo"]}');
INSERT INTO jsonthings VALUES ('{"name":"Second","tags":["foo","bar"]}');
INSERT INTO jsonthings VALUES ('{"name":"Third","tags":["bar","baz"]}');
INSERT INTO jsonthings VALUES ('{"name":"Fourth","tags":["baz"]}');

CREATE INDEX ON jsonthings USING GIN(d);
Run Code Online (Sandbox Code Playgroud)

并且在运行SELECT. SELECT获取值为单个项目的行的简单方法工作正常:

SELECT d FROM jsonthings WHERE d @> '{"name":"First"}';
Run Code Online (Sandbox Code Playgroud)

但是当尝试运行匹配多个值的查询时,name我无法找到如何使用索引。我试过了:

SELECT d FROM jsonthings WHERE d->>'name' = ANY(ARRAY['First', 'Second']);
SELECT d FROM jsonthings WHERE d->'name' ?| ARRAY['First', 'Second'];
SELECT d FROM jsonthings WHERE d#>'{name}' ?| ARRAY['First','Second'];
Run Code Online (Sandbox Code Playgroud)

并且所有这些都显示了对表的顺序扫描(enable_seqscan=false如果可能,我正在使用强制使用索引)。有什么方法可以重写查询以便它使用索引?我知道我可以这样做:

SELECT * FROM jsonthings WHERE d @> '{"name":"First"}' OR d @> '{"name":"Second"}';
Run Code Online (Sandbox Code Playgroud)

但后来我有一个可变长度的查询,我正在通过 JDBC,所以会失去查询作为 PreparedStatement 的好处。

我也有兴趣看到针对键中任何项目的类似查询tags,例如:

SELECT d FROM jsonthings WHERE d @> '{"tags":["foo"]}' OR d @> '{"tags":["bar"]}';
Run Code Online (Sandbox Code Playgroud)

但使用一个ARRAY而不是多个条件并使用索引。

这是在 PostgreSql 9.4 上。

小智 8

这是对 Mladen 提供的答案的回应。我没有足够的声誉来发表评论,但我想回复,因为看起来查询可能不正确,并且让我感到困惑,并且可能会导致其他人将来感到困惑。

你提到使用:

SELECT d FROM jsonthings WHERE d @> '{"name": ["First", "Second"]}';
Run Code Online (Sandbox Code Playgroud)

然而,要检索具有FirstSecond作为名称的任何条目,这似乎对我不起作用PostgreSQL 9.4.4

SELECT d FROM jsonthings WHERE d @> '{"name": ["First", "Second"]}';
 d
---
(0 rows)
Run Code Online (Sandbox Code Playgroud)

上面的查询似乎正在尝试检索属性name包含 array 的条目["First", "Second"]

如果我创建这样一个条目:

INSERT INTO jsonthings VALUES ('{"name":["First", "Second"],"tags":["baz"]}');
Run Code Online (Sandbox Code Playgroud)

然后再次尝试查询,它返回结果:

SELECT d FROM jsonthings WHERE d @> '{"name": ["First", "Second"]}';
d
------------------------------------------------
{"name": ["First", "Second"], "tags": ["baz"]}
(1 row)
Run Code Online (Sandbox Code Playgroud)

name但是,这与原始发布者提出的问题不同,原始发布者提出的问题是在查询属性为First or 的 条目时如何使用索引Second

SELECT * FROM jsonthings WHERE d @> '{"name":"First"}' OR d @> '{"name":"Second"}';
Run Code Online (Sandbox Code Playgroud)

我想在这里提供这个,这样其他人就不会认为可以通过提供 来使用 JSON 执行OR查询"name": ["First", "Second"],因为它具有误导性。


Mla*_*lac 4

从文档(http://www.postgresql.org/docs/9.4/static/datatype-json.html)尝试使用表达式索引:

CREATE INDEX idx_jsonthings_names ON jsonthings USING gin ((d -> 'name'));
SELECT d FROM jsonthings WHERE d @> '{"name": ["First", "Second"]}';
Run Code Online (Sandbox Code Playgroud)

  • 是的,看起来我确实必须使用单独的索引,这看起来很奇怪,因为该索引显然已经存在并且在单项查询中使用。我需要使用的查询是`SELECT d FROM jsonthings WHERE d->'name' ?| ARRAY['First', 'Second'];` 否则不使用索引。谢谢。 (2认同)