检查Postgres JSON数组是否包含字符串

Sno*_*all 86 postgresql json postgresql-9.3

我有一张桌子来存储关于我的兔子的信息.它看起来像这样:

create table rabbits (rabbit_id bigserial primary key, info json not null);
insert into rabbits (info) values
  ('{"name":"Henry", "food":["lettuce","carrots"]}'),
  ('{"name":"Herald","food":["carrots","zucchini"]}'),
  ('{"name":"Helen", "food":["lettuce","cheese"]}');
Run Code Online (Sandbox Code Playgroud)

我该怎么找到喜欢胡萝卜的兔子?我想出了这个:

select info->>'name' from rabbits where exists (
  select 1 from json_array_elements(info->'food') as food
  where food::text = '"carrots"'
);
Run Code Online (Sandbox Code Playgroud)

我不喜欢那个查询.一团糟.

作为一名全职兔子守护者,我没有时间改变我的数据库架构.我只想喂我的兔子.是否有更可读的方式来执行该查询?

Sno*_*all 143

从PostgreSQL 9.4开始,您可以使用?运算符:

select info->>'name' from rabbits where (info->'food')::jsonb ? 'carrots';
Run Code Online (Sandbox Code Playgroud)

如果切换到jsonb类型,您甚至可以?"food"键上索引查询:

alter table rabbits alter info type jsonb using info::jsonb;
create index on rabbits using gin ((info->'food'));
select info->>'name' from rabbits where info->'food' ? 'carrots';
Run Code Online (Sandbox Code Playgroud)

当然,作为一名全职兔子守门员,你可能没有时间.

更新:这里展示了1,000,000只兔子的表格,每只兔子喜欢两种食物,其中10%像胡萝卜:

d=# -- Postgres 9.3 solution
d=# explain analyze select info->>'name' from rabbits where exists (
d(# select 1 from json_array_elements(info->'food') as food
d(#   where food::text = '"carrots"'
d(# );
 Execution time: 3084.927 ms

d=# -- Postgres 9.4+ solution
d=# explain analyze select info->'name' from rabbits where (info->'food')::jsonb ? 'carrots';
 Execution time: 1255.501 ms

d=# alter table rabbits alter info type jsonb using info::jsonb;
d=# explain analyze select info->'name' from rabbits where info->'food' ? 'carrots';
 Execution time: 465.919 ms

d=# create index on rabbits using gin ((info->'food'));
d=# explain analyze select info->'name' from rabbits where info->'food' ? 'carrots';
 Execution time: 256.478 ms
Run Code Online (Sandbox Code Playgroud)

  • 有谁知道这是如何工作的,以防您需要选择整数而不是字符串/文本? (4认同)
  • 使用 JSONB 作为 `info`,使用 `where info @> '{"food":["carrots"]}'` 不是更好吗?这使用了 `info` 列上的 GIN 索引,同时使用 `->` (如 `ext->'hobbies' @> '"eating"'`)来阻止它。这意味着不需要对 JSON 键建立索引,只需对整个列建立一次索引(当然,如果“包含”操作就足够了)。 (4认同)
  • @Bravo `select * from rabbits where info->'food' != '[]';` (2认同)
  • @Rotareti您可以使用[@>运算符](https://www.postgresql.org/docs/9.4/functions-json.html#FUNCTIONS-JSONB-OP-TABLE):`create table t(x jsonb); 插入t(x)个值('[1,2,3]'),('[2,3,4]'),('[3,4,5]'); 从t中选择*,其中x @>'2';`。请注意,“ 2”是一个JSON编号;不要被引号误导。 (2认同)

chr*_*mod 17

不聪明但更简单:

select info->>'name' from rabbits WHERE info->>'food' LIKE '%"carrots"%';
Run Code Online (Sandbox Code Playgroud)

  • 如果您将胡萝卜作为子字符串并具有其他上下文,例如: `{"name":"Henry", "food":["lettuce","foocarrots"]}` ,那么记录又如何呢? (2认同)

gor*_*ori 17

您可以使用@>运算符来执行此操作

SELECT info->>'name'
FROM rabbits
WHERE info->'food' @> '"carrots"';
Run Code Online (Sandbox Code Playgroud)

  • 确保你注意“胡萝卜”周围的`'`滴答声......如果你忽略它们,即使你正在检查一个整数,它也会中断。(花了 3 个小时试图找到一个整数,通过在数字周围包裹 `'` 勾号让它神奇地工作) (2认同)

小智 16

如果数组位于 jsonb 列的根部,则 ie 列如下所示:

食物
[“生菜”、“胡萝卜”]
[“胡萝卜”、“西葫芦”]

只需直接在括号内使用列名称即可:

select * from rabbits where (food)::jsonb ? 'carrots';
Run Code Online (Sandbox Code Playgroud)


mac*_*ias 12

一个小的变化,但没有什么新的事实.它真的缺少一个功能......

select info->>'name' from rabbits 
where '"carrots"' = ANY (ARRAY(
    select * from json_array_elements(info->'food'))::text[]);
Run Code Online (Sandbox Code Playgroud)


cbe*_*tez 6

为了选择JSONB中的特定键,您应该使用->

select * from rabbits where (info->'food')::jsonb ? 'carrots';
Run Code Online (Sandbox Code Playgroud)


mar*_*uru 5

如果您想检查完整的 json 而不是一个键,您可以直接从 jsonb 进行类型转换为文本。

select * from table_name
where 
column_name::text ilike '%Something%';
Run Code Online (Sandbox Code Playgroud)