44

我在 postgresql 9.4 数据库中有一个表,其中有一个名为接收器的 jsonb 字段。一些示例行:

[{"id": "145119603", "name": "145119603", "type": 2}]
[{"id": "1884595530", "name": "1884595530", "type": 1}]
[{"id": "363058213", "name": "363058213", "type": 1}]
[{"id": "1427965764", "name": "1427965764", "type": 1}]
[{"id": "193623800", "name": "193623800", "type": 0}, {"id": "419955814", "name": "419955814", "type": 0}]
[{"id": "624635532", "name": "624635532", "type": 0}, {"id": "1884595530", "name": "1884595530", "type": 1}]
[{"id": "791712670", "name": "791712670", "type": 0}]
[{"id": "895207852", "name": "895207852", "type": 0}]
[{"id": "144695994", "name": "144695994", "type": 0}, {"id": "384217055", "name": "384217055", "type": 0}]
[{"id": "1079725696", "name": "1079725696", "type": 0}]

我有一个 id 值列表,并希望在 jsonb 字段的数组中选择包含具有该列表中任何值的对象的任何行。

那可能吗?我可以制作一个 GIN 索引来加快速度吗?

4

2 回答 2

72

没有单一的操作可以帮助您,但您有几个选择:

1.如果要查询的 id 数量很少(且固定),可以使用多个包含运算符@>结合or; 例:

where data @> '[{"id": "1884595530"}]' or data @> '[{"id": "791712670"}]'

一个简单的gin索引可以帮助您在这里的数据列。

2.如果你有可变数量的 ids(或者你有很多),你可以用它json[b]_array_elements()来提取数组的每个元素,建立一个 id 列表,然后用任何包含操作符查询它?|

select *
from   jsonbtest
where  to_json(array(select jsonb_array_elements(data) ->> 'id'))::jsonb ?|
         array['1884595530', '791712670'];

不幸的是,您无法索引其中包含子查询的表达式。如果你想索引它,你需要为它创建一个函数:

create function idlist_jsonb(jsonbtest)
  returns jsonb
  language sql
  strict
  immutable
as $func$
  select to_json(array(select jsonb_array_elements($1.data) ->> 'id'))::jsonb
$func$;

create index on jsonbtest using gin (idlist_jsonb(jsonbtest));

在此之后,您可以像这样查询 id:

select *, jsonbtest.idlist_jsonb
from   jsonbtest
where  jsonbtest.idlist_jsonb ?| array['193623800', '895207852'];

注意:我在这里使用了点表示法/计算字段,但您不必这样做。

3.但此时,您不必拘泥于 json[b]:您有一个简单的文本数组,PostgreSQL 也支持该数组。

create function idlist_array(jsonbtest)
  returns text[]
  language sql
  strict
  immutable
as $func$
  select array(select jsonb_array_elements($1.data) ->> 'id')
$func$;

create index on jsonbtest using gin (idlist_array(jsonbtest));

并使用重叠数组运算符查询此计算字段&&

select *, jsonbtest.idlist_array
from   jsonbtest
where  jsonbtest.idlist_array && array['193623800', '895207852'];

注意:从我的内部测试来看,后一种解决方案的计算成本比 jsonb 变体要高,但实际上它比这快一点。如果性能对您来说真的很重要,您应该同时测试两者。

于 2015-02-13T10:22:15.310 回答
11

我找到解决方法:
where data::text similar to '%("id": "145119603"|"id": "1884595530")%'

于 2017-06-22T15:41:14.500 回答