我在PostgreSQL数据库(v 9.6)中有一个相当简单的表:
CREATE TABLE foobar (id serial, data jsonb);
以下是存储在数据列中的JSONB文档示例:
[{ key: 'foo', value: 100 }, { key: 'bar', value: 5 }, { key: 'baz', value: 10 }]
我正在尝试编写一个select,它将返回满足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)
如果有人知道如何优化最终查询,那也很棒。谢谢!
答案 0 :(得分:3)
对于foo大于X的每一行,返回所有以baz为键的嵌套文档。
两次使用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)
的对象:
'{ "foo": 100, "bar": 5, "baz": 10 }'
你的json专栏的格式很奇怪。我认为没有理由在这里使用json数组。您可以使用以下简单形式存储相同的信息:
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)
在这种情况下,您的查询可能非常简单:
ctx.instance.items = undefined;
答案 1 :(得分:0)
您可以通过以下方式实现此目的:
jsonb_array_elements
function)HAVING bool_or(...)
确保至少有一个元素的关键字为foo
且value
大于5
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索引的用例。使用您当前的设计,顺序表扫描是不可避免的(除非上面的谓词实际上可以过滤掉很多行)。