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.