按嵌套 JSONB 文档中的条件查询数据集
Posted
技术标签:
【中文标题】按嵌套 JSONB 文档中的条件查询数据集【英文标题】:Querying dataset by conditions in nested JSONB document 【发布时间】:2017-05-26 19:57:55 【问题描述】:我在 PostgreSQL 数据库 (v 9.6) 中有一个相当简单的表:
CREATE TABLE foobar (id serial, data jsonb);
以下是存储在数据列中的 JSONB 文档示例:
[ key: 'foo', value: 100 , key: 'bar', value: 5 , key: 'baz', value: 10 ]
我正在尝试编写一个选择,它将返回满足 JSONB 文档中条件的每一行并仅选择指定的嵌套文档;即对于foo
大于X
的每一行,返回所有以baz
为键的嵌套文档。
到目前为止,我已经提出了这样的查询,但它不起作用 - 它返回 0 条记录。
SELECT id
FROM foobar
WHERE (data->>'key' = 'foo' AND (data->>'value')::numeric > 5)
OR (data->>'key' = 'bar' AND (data->>'value')::numeric < 10)
如果有人知道如何优化最终查询,那就太好了。谢谢!
【问题讨论】:
使用>=
和<=
?
【参考方案1】:
对于 foo 大于 X 的每一行,返回所有以 baz 为键的嵌套文档。
使用jsonb_array_elements(data)
两次。第一个用于与键 foo
比较值,第二个用于查找与键 baz
的对象:
with foobar(id, data) as (
values
(1,
'[
"key": "foo", "value": 100 ,
"key": "bar", "value": 5 ,
"key": "baz", "value": 10
]'::jsonb)
)
select id, value_baz
from foobar,
jsonb_array_elements(data) el_foo(value_foo),
jsonb_array_elements(data) el_baz(value_baz)
where value_foo->>'key' = 'foo' and (value_foo->>'value')::numeric > 5
and value_baz->>'key' = 'baz';
id | value_baz
----+-----------------------------
1 | "key": "baz", "value": 10
(1 row)
您的 json 列的格式很奇怪。我认为没有理由在这里使用 json 数组。您可以像这样以简单的形式存储相同的信息:
' "foo": 100, "bar": 5, "baz": 10 '
在这种情况下,您的查询可能很简单:
with foobar(id, data) as (
values
(1, ' "foo": 100, "bar": 5, "baz": 10 '::jsonb)
)
select id, data->'baz' as baz
from foobar
where (data->>'foo')::numeric > 5;
id | baz
----+-----
1 | 10
(1 row)
【讨论】:
是的,第二种解决方案要好得多;我最初已经尝试过,并在数据上使用 gin 索引实现了约 200 毫秒的选择(我确实有一个相当大的数据集)。第一个选项在我的数据集上不起作用 - 它是否太大并且选择需要超过 30 秒【参考方案2】:您可以通过以下方式实现:
将 JSON 数组取消嵌套到元素中(使用 thejsonb_array_elements
function)
HAVING bool_or(...)
确保至少有一个元素以 foo
为键且 value
大于 5
aggregate filtered:仅那些以bar
为键的子文档
SELECT id, jsonb_agg(e) FILTER (WHERE e ->> 'key' = 'bar')
FROM foobar
CROSS JOIN jsonb_array_elements(data) e
WHERE data @> '["key":"foo","key":"bar"]'
GROUP BY id
HAVING bool_or(e ->> 'key' = 'foo' AND e -> 'value' > '5')
注意事项:
e -> 'value' > '5'
会将 value
JSON 属性与 JSON 值 5
进行比较,如果您在 JSON 文档中使用数字,这将非常有效。如果不是这种情况(即您也有 JSON 字符串,您想将其作为数字处理),请使用 CAST
,例如 (e ->> 'value')::numeric > 5
WHERE data @> '["key":"foo","key":"bar"]'
是完全可选的。我在这里包括了,因为这是您可以从GIN index(在jsonb
列上;如果有的话)中挤出的最多内容。但是,如果这些键几乎在您的表中的每一行上,那将几乎没有用。
不幸的是,每个解决方案都需要使用jsonb_array_elements()
,这违背了 JSON 索引的用例。在您当前的设计中,顺序表扫描是不可避免的(除非上面的谓词实际上可以过滤掉很多行)。
http://rextester.com/XCPB80482
【讨论】:
以上是关于按嵌套 JSONB 文档中的条件查询数据集的主要内容,如果未能解决你的问题,请参考以下文章
如何根据postgres的jsonb列的where条件(无本机查询)使用jpa在spring boot中选择数据?
查询包含(或不包含)嵌套文档数组(被填充)但匹配特定条件的文档