JSON 배열을 postgres 배열로 바꾸는 방법은 무엇입니까?


69

대략 다음과 같은 문서 data를 보유 하는 열 json이 있습니다.

{
    "name": "foo",
    "tags": ["foo", "bar"]
}

중첩 tags배열을 연결된 문자열 ( foo, bar) 로 바꾸고 싶습니다 . array_to_string()이론 상으로는 쉽게 가능합니다 . 그러나이 함수는 json배열 에서 작동하지 않습니다 . 그래서이 json배열을 Postgres로 바꾸는 방법이 궁금합니다 array.


json_extract_path_text(your_column, 'tags') 당신이 찾고있는 무엇?
a_horse_with_no_name

1
@a_horse_with_no_name : 나머지 문제 : 배열 요소는 여전히 JSON 형식으로 인용됩니다. 텍스트가 제대로 추출되지 않습니다 ...
Erwin Brandstetter

답변:


94

Postgres 9.4 이상

이 글 에서 분명히 영감을 얻은 Postgres 9.4는 누락 된 기능을 추가
했습니다. 패치에 대한 Laurence Rowe와 커밋에 대한 Andrew Dunstan에게 감사드립니다!

JSON 배열을 숨기려면 그런 다음 array_agg()또는 ARRAY 생성자 를 사용하여 Postgres 배열 을 빌드 하십시오. 또는 string_agg()구축하는 text 문자열 .

LATERAL상관 된 하위 쿼리 에서 행당 중첩되지 않은 요소를 집계 합니다. 그런 다음 원래의 순서는 유지됩니다 우리는 필요하지 않습니다 ORDER BY, GROUP BY또는 외부 쿼리의 경우에도 고유 키. 보다:

jsonb다음의 모든 SQL 코드에서 'json'을 'jsonb'로 바꾸십시오 .

SELECT t.tbl_id, d.list
FROM   tbl t
CROSS  JOIN LATERAL (
   SELECT string_agg(d.elem::text, ', ') AS list
   FROM   json_array_elements_text(t.data->'tags') AS d(elem)
   ) d;

짧은 구문 :

SELECT t.tbl_id, d.list
FROM   tbl t, LATERAL (
   SELECT string_agg(value::text, ', ') AS list
   FROM   json_array_elements_text(t.data->'tags')  -- col name default: "value"
   ) d;

관련 :

상관 하위 쿼리의 ARRAY 생성자 :

SELECT tbl_id, ARRAY(SELECT json_array_elements_text(t.data->'tags')) AS txt_arr
FROM   tbl t;

관련 :

미묘한 차이 : null요소는 실제 배열 로 유지됩니다 . text문자열을 생성하는 위의 쿼리에서는 불가능하며 null값을 포함 할 수 없습니다 . 진정한 표현 배열이다.

함수 래퍼

반복적 인 사용을 위해 이것을 훨씬 더 간단하게하기 위해 함수에 논리를 캡슐화하십시오.

CREATE OR REPLACE FUNCTION json_arr2text_arr(_js json)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT json_array_elements_text(_js))';

더 큰 쿼리에서 인라인 될 수 있도록 SQL 함수로 만드십시오 . 더 큰 쿼리에서 반복적 인 평가를 피하고 인덱스 표현식에 허용하려면 (그렇기 때문에) 확인하십시오 .
IMMUTABLE

요구:

SELECT tbl_id, json_arr2text_arr(data->'tags')
FROM   tbl;

db <> 바이올린 여기


Postgres 9.3 이상

기능을 사용하십시오 json_array_elements(). 그러나 우리는 큰 따옴표로 묶인 문자열 을 얻 습니다.

외부 쿼리에서 집계가 포함 된 대체 쿼리 CROSS JOIN누락되거나 비어있는 배열이있는 행을 제거합니다. 요소 처리에도 유용 할 수 있습니다. 집계하려면 고유 키가 필요합니다.

SELECT t.tbl_id, string_agg(d.elem::text, ', ') AS list
FROM   tbl t
CROSS  JOIN LATERAL json_array_elements(t.data->'tags') AS d(elem)
GROUP  BY t.tbl_id;

따옴표로 묶인 문자열이있는 ARRAY 생성자 :

SELECT tbl_id, ARRAY(SELECT json_array_elements(t.data->'tags')) AS quoted_txt_arr
FROM   tbl t;

그 주 null위는 달리, 텍스트 값 "널 (null)"로 변환됩니다. 부정확하고 엄격하게 말하며 잠재적으로 모호 할 수 있습니다.

불쌍한 사람의 인용 부호 trim():

SELECT t.tbl_id, string_agg(trim(d.elem::text, '"'), ', ') AS list
FROM   tbl t, json_array_elements(t.data->'tags') d(elem)
GROUP  BY 1;

tbl에서 단일 행을 검색하십시오.

SELECT string_agg(trim(d.elem::text, '"'), ', ') AS list
FROM   tbl t, json_array_elements(t.data->'tags') d(elem)
WHERE  t.tbl_id = 1;

문자열은 상관 된 하위 쿼리를 형성합니다.

SELECT tbl_id, (SELECT string_agg(trim(value::text, '"'), ', ')
                FROM   json_array_elements(t.data->'tags')) AS list
FROM   tbl t;

ARRAY 생성자 :

SELECT tbl_id, ARRAY(SELECT trim(value::text, '"')
                     FROM   json_array_elements(t.data->'tags')) AS txt_arr
FROM   tbl t;

원본 (오래된) SQL Fiddle .
db <> 바이올린 .

관련 :

참고 사항 (9 월 4 일 이후로 만료 됨)

JSON 배열에서 적절한 값 을 반환 json_array_elements_text(json)하려면의 쌍이 필요합니다 . 그러나 그것은 제공되는 JSON 함수의 무기고 에서 누락 된 것 같습니다 . 또는 스칼라 값 에서 값 을 추출하는 다른 함수 입니다. 나도 그 누락 된 것 같습니다. 그래서 나는 즉흥적으로 했지만 사소하지 않은 경우에는 실패합니다 ...json_array_elements(json)texttextJSON
trim()


항상 좋은 게시물이지만 내부에 대한 지식이 있으면 왜 array-> jsonb에서 캐스트되지 않습니까? sql-array가 더 강력하게 입력되었으므로 다른 캐스트를 구현하지 않는 것을 이해할 수 있습니다. PostgreSQL이 (int [], bigint [], text []) 캐스트 할 코드를 자동 생성하는 것을 싫어하기 때문입니다.
Evan Carroll

3
to_jsonb()@Evan : 배열-> jsonb 변환에 사용합니다.
Erwin Brandstetter

SELECT ARRAY(SELECT json_array_elements_text(_js))실제로 배열의 순서가 유지되도록 보장 합니까 ? 옵티마이 저가 이론적으로 json_array_elements_text에서 나오는 행의 순서를 변경할 수 있습니까?
펠릭스 Geisendörfer

@Felix : SQL 표준에는 공식적인 보증이 없습니다. (다시, set return 함수는 표준 SQL의 SELECT 목록에서도 허용되지 않습니다.) 그러나 Postgres 매뉴얼에는 비공식적 주장이 있습니다. 참조 : dba.stackexchange.com/a/185862/3684이 명시하기 - 마이너 perfomance 처벌의 비용 - 참조 : dba.stackexchange.com/a/27287/3684를 . 개인적으로, 나는이 특정 표현이 9.4 이후 모든 현재 및 향후 버전의 Postgres에서 예상대로 작동한다고 100 % 확신합니다.
Erwin Brandstetter

@ErwinBrandstetter 이것을 확인해 주셔서 감사합니다! 현재 PostgreSQL에서 제공하는 공식 보증 및 비공식 보증 주문 보증을 요약 한 기사를 연구 중이며 귀하의 답변이 매우 도움이되었습니다! 당신이 기사를 검토에 관심이 있다면 알려 주시기 바랍니다,하지만 걱정하지 마십시오. StackOverflow 기여에 대해 대단히 감사하며 수년 동안 많은 것을 배웠습니다!
펠릭스 Geisendörfer

16

PG 9.4+

받아 들여진 대답은 분명히 당신이 필요로하는 것이지만, 단순화를 위해 여기에 내가 사용하는 도우미가 있습니다.

CREATE OR REPLACE FUNCTION jsonb_array_to_text_array(
  p_input jsonb
) RETURNS TEXT[] AS $BODY$

DECLARE v_output text[];

BEGIN

  SELECT array_agg(ary)::text[]
  INTO v_output
  FROM jsonb_array_elements_text(p_input) AS ary;

  RETURN v_output;

END;

$BODY$
LANGUAGE plpgsql VOLATILE;

그런 다음 수행하십시오.

SELECT jsonb_array_to_text_array('["a", "b", "c"]'::jsonb);

내 답변에 더 빠른 표현과 간단한 기능을 추가했습니다. 이것은 실질적으로 더 저렴할 수 있습니다.
Erwin Brandstetter

4
옵티마이 저가이를 엿볼 수 있도록이 함수는 순수 SQL이어야합니다. 여기서 pgplsql을 사용할 필요가 없습니다.
나누기

8

이 질문은 PostgreSQL 메일 링리스트 에서 요청되었으며 JSON 필드 추출 연산자를 통해 JSON 텍스트를 PostgreSQL 텍스트 유형으로 변환하는 해킹 방법을 생각해 냈습니다.

CREATE FUNCTION json_text(json) RETURNS text IMMUTABLE LANGUAGE sql
AS $$ SELECT ('['||$1||']')::json->>0 $$;

db=# select json_text(json_array_elements('["hello",1.3,"\u2603"]'));
 json_text 
-----------
 hello
 1.3
 

기본적으로 값을 단일 요소 배열로 변환 한 다음 첫 번째 요소를 요청합니다.

다른 방법은이 연산자를 사용하여 모든 필드를 하나씩 추출하는 것입니다. 그러나 큰 배열의 경우 각 배열 요소에 대한 전체 JSON 문자열을 구문 분석해야하므로 O (n ^ 2) 복잡성이 발생하므로 속도가 느려질 수 있습니다.

CREATE FUNCTION json_array_elements_text(json) RETURNS SETOF text IMMUTABLE LANGUAGE sql
AS $$ SELECT $1->>i FROM generate_series(0, json_array_length($1)-1) AS i $$;

db=# select json_array_elements_text('["hello",1.3,"\u2603"]');
 json_array_elements_text 
--------------------------
 hello
 1.3
 

1

몇 가지 옵션을 테스트했습니다. 내가 가장 좋아하는 검색어는 다음과 같습니다. id와 json 필드를 포함하는 테이블이 있다고 가정하십시오. json 필드에는 pg 배열로 바꾸려는 배열이 있습니다.

SELECT * 
FROM   test 
WHERE  TRANSLATE(jsonb::jsonb::text, '[]','{}')::INT[] 
       && ARRAY[1,2,3];

그것은 다른 곳보다 더 빠르게 작동하지만 목발처럼 보입니다)

먼저 json 배열은 텍스트로 캐스팅 된 다음 대괄호를 괄호로 변경합니다. 마지막으로 텍스트는 필수 유형의 배열로 캐스트됩니다.

SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::INT[];

그리고 text [] 배열을 선호한다면

SELECT TRANSLATE('[1]'::jsonb::text, '[]','{}')::TEXT[];

2
SELECT TRANSLATE('{"name": "foo", "tags": ["foo", "bar"]}'::jsonb::text, '[]','{}')::INT[]; ERROR: malformed array literal: "{"name": "foo", "tags": {"foo", "bar"}}"이것이 어떻게 작동하는지에 대한 설명을 추가해야한다고 생각합니다.
dezso

문제는 JSON 배열 (!)을 pg 배열로 바꾸는 방법입니다. id 및 jsonb 열을 포함하는 테이블이 있다고 가정하십시오. JSONb 열에는 json 배열이 포함됩니다. 그때
FiscalCliff

TRANSLATE (jsonb :: jsonb :: text, '[]', '{}') :: INT []는 json 배열을 pg 배열로 변환합니다.
FiscalCliff

SELECT translate('["foo", "bar"]'::jsonb::text, '[]','{}')::INT[]; ERROR: invalid input syntax for integer: "foo"방폭형은 아닙니다 ...
dezso

이 배열에 text [] 사용을 고려하십시오
FiscalCliff

0

이 질문에 대한 답변에서 가져온 몇 가지 기능 은 내가 사용하고 있으며 잘 작동하고 있습니다.

CREATE OR REPLACE FUNCTION json_array_casttext(json) RETURNS text[] AS $f$
    SELECT array_agg(x) || ARRAY[]::text[] FROM json_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION jsonb_array_casttext(jsonb) RETURNS text[] AS $f$
    SELECT array_agg(x) || ARRAY[]::text[] FROM jsonb_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION json_array_castint(json) RETURNS int[] AS $f$
    SELECT array_agg(x)::int[] || ARRAY[]::int[] FROM json_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION jsonb_array_castint(jsonb) RETURNS int[] AS $f$
    SELECT array_agg(x)::int[] || ARRAY[]::int[] FROM jsonb_array_elements_text($1) t(x);
$f$ LANGUAGE sql IMMUTABLE;

그들 각각에, 하늘의 배열과 연결하여, 그들은 당신이 시도에서 하늘의 배열을 시전하면 저 점에서, 조금 내 머리를 건 드리는 한 경우 처리 json/ jsonb대신에, 반환 당신이 아무것도 얻을 것없이를 {}예상대로 빈 배열 ( ). 나는 그것들에 대한 최적화가 있다고 확신하지만 개념을 설명하는 데있어 간단하게 남아 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.