Проверьте, содержит ли массив Postgres JSON строку
У меня есть таблица для хранения информации о моих кроликах. Это выглядит так:
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
Как и в PostgreSQL 9.4, вы можете использовать оператор ?
:
select info->>'name' from rabbits where (info->'food')::jsonb ? 'carrots';
Вы даже можете индексировать запрос ?
в ключе "food"
, если вместо этого вы переключаетесь на тип jsonb:
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
Ответ 2
Не умнее, но проще:
select info->>'name' from rabbits WHERE info->>'food' LIKE '%"carrots"%';
Ответ 3
Небольшая вариация, но ничего нового. В нем действительно отсутствует функция...
select info->>'name' from rabbits
where '"carrots"' = ANY (ARRAY(
select * from json_array_elements(info->'food'))::text[]);
Ответ 4
Вы можете использовать оператор @ > , чтобы сделать что-то вроде
SELECT info->>'name'
FROM rabbits
WHERE info->'food' @> '"carrots"';
Ответ 5
Как насчет того, если у вас есть вложенная структура поля выше: Like: ('{"name": "Henry", {"food": ["lettuce", "carrots"], "food1": ["lettuce", "" морковь "]}} '),