Postgres JSON 배열에 문자열이 포함되어 있는지 확인


121

내 토끼에 대한 정보를 저장할 테이블이 있습니다. 다음과 같이 보입니다.

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 : (질문을 업데이트했습니다.) 효율성보다 가독성을 선호합니다. 스키마를 고정하고 있기 때문에 전체 테이블 스캔보다 더 나은 것을 기대하지는 않습니다.
Snowball 2013

11
내가 토끼 ​​때문에이 질문에 찬성 한 것이 잘못입니까?
osman

3
난 그냥 때문에 토끼의이 질문을 upvoted 다음 주석 @osman를 보았다
1valdis

나는 당신의 코멘트를보고 토끼 때문에 찬성해야한다는 것을 깨달았습니다
Peter Aron Zentai

답변:


187

PostgreSQL 9.4부터 다음 ?연산자를 사용할 수 있습니다 .

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';

물론, 당신은 풀 타임 토끼 사육자로서 그럴 시간이 없을 것입니다.

업데이트 : 다음은 각 토끼가 두 가지 음식을 좋아하고 그중 10 %가 당근을 좋아하는 토끼 1,000,000 마리의 테이블에서 성능 향상을 보여줍니다.

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 내부의 음식 배열이 비어 있지 않은 행을 가져 오는 방법, 예를 들어 고려할 수 있다면, 그것들은 JSON이고, 음식 배열도 비어 있습니다. 도와 주실 수 있습니까?
Bravo

1
@Bravoselect * from rabbits where info->'food' != '[]';
눈덩이

1
문자열 / 텍스트 대신 정수를 선택해야하는 경우 이것이 어떻게 작동하는지 아는 사람이 있습니까?
Rotareti 2018

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 2018

@Snowball,이 쿼리는 토끼에서 info->> 'name'을 선택합니다. where (info-> 'food') :: jsonb? '당근'; JSON의 검색어에 완벽하게 작동합니다. 하지만 어떻게 모든 기록에 '당근'단어가 포함되어 있지 않습니까?
Milan

23

@> 연산자를 사용하여 다음과 같이 할 수 있습니다.

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

1
항목이 null 인 경우에도 유용합니다.
Lucio

2
'"당근"을 둘러싼 진드기에 주의를 기울이십시오 . 정수를 확인하는 경우에도이를 생략하면 깨집니다. (그것을 가지고, 정수를 찾기 위해 노력하고 3 시간 동안 마법 포장하여 작품 '수를 약 틱)
skplunkerin

@skplunkerin '모든 것이 JSONB 유형의 SQL 문자열이기 때문에 문자열을 형성하려면 눈금으로 둘러싸인 json 값 이어야합니다. 예를 들어 boolean : 'true', string : '"example"', integer : '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[]);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.