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


119

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

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"]}');

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

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

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

作为专职兔子管理员,我没有时间更改数据库架构。我只想适当地喂兔子。有没有更可读的方法来执行该查询?


1
有趣的问题。我玩过它,但是后来它突然出现在我身上,我不确定你所说的“更好”是什么意思。您根据什么标准判断答案?可读性?效率?其他?
David S

@DavidS :(我更新了问题。)我宁愿可读性胜于效率。我当然不希望有比全表扫描更好的功能,因为我将模式保持不变。
雪球2013年

11
我因为兔子而投票支持这个问题是错误的吗?
奥斯曼

3
我只是因为兔子就提出了这个问题,然后看到了您的评论@osman
1valdis

我看到了您的评论,然后意识到我需要因为兔子而投票
Peter Aron Zentai

Answers:


184

从PostgreSQL 9.4开始,您可以使用?operator

select info->>'name' from rabbits where (info->'food')::jsonb ? 'carrots';

如果改用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';

当然,作为专职兔子饲养员,您可能没有时间这样做。

更新:这是在一张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

如何获取json中food数组不为空的行,例如,如果我们可以考虑,它们为JSON,food数组也为空的行,您能帮忙吗
Bravo

1
@Bravoselect * from rabbits where info->'food' != '[]';
雪球游戏

1
有谁知道在需要选择整数而不是字符串/文本的情况下这是如何工作的?
Rotareti '18

3
@Rotareti您可以使用@>运算符create table t (x jsonb); insert into t (x) values ('[1,2,3]'), ('[2,3,4]'), ('[3,4,5]'); select * from t where x @> '2';。请注意,这'2'是一个JSON数字;不要被引号误导。
雪球

@Snowball,此查询从兔子那里选择info->>'name',其中(info->'food'):: jsonb吗?'萝卜'; 非常适合JSON中的搜索词。但是,如何获取所有不包含“胡萝卜”字眼的记录?
米兰,

23

您可以使用@>运算符执行类似

SELECT info->>'name'
FROM rabbits
WHERE info->'food' @> '"carrots"';

1
当该项也为null时,这很有用
Lucio

2
确保您注意'“胡萝卜”周围的滴答声……如果忽略这些滴答声,即使您正在检查整数,它也会中断。(花了3个小时试图找到一个整数,通过'在数字周围加上刻度线使其神奇地工作)
skplunkerin

@skplunkerin应该是json值,并在其之间加上'刻度线以形成字符串,因为对于JSONB类型的SQL来说,所有内容都是字符串。例如,布尔值:'true',字符串:'"example"',整数:'123'
1valdis

22

不聪明但更简单:

select info->>'name' from rabbits WHERE info->>'food' LIKE '%"carrots"%';

13

很小的变化,但没有新的事实。确实缺少功能...

select info->>'name' from rabbits 
where '"carrots"' = ANY (ARRAY(
    select * from json_array_elements(info->'food'))::text[]);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.