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形式で引用されています。テキストが正しく抽出されていません...
アーウィンBrandstetterを

回答:


94

Postgres 9.4以降

明らかにこの投稿触発されて、Postgres 9.4は足りない機能を追加し
ました。パッチのLaurence RoweとコミットしてくれたAndrew Dunstanに感謝します!

JSON配列のネストを解除します。次に、array_agg()またはARRAYコンストラクターを使用して、Postgres 配列を構築します。またはstring_agg()text 文字列を作成します

LATERALまたは相関サブクエリの行ごとにネストされていない要素を集約します。その後、元の順序は保持され、外部クエリにORDER BYGROUP 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要素は実際の配列に保存されます。これはtextnull値を含むことができない文字列を生成する上記のクエリでは不可能です。真の表現は配列です。

関数ラッパー

繰り返し使用する場合は、これをさらに簡単にするために、ロジックを関数にカプセル化します。

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 <> fiddle here


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 <> fiddle here。

関連する:

注意事項(pg 9.4から廃止)

JSON配列から適切な値を返すにはjson_array_elements_text(json)、が必要です。しかし、それはJSON関数の提供された兵器庫から欠落しているようです。または、スカラー値から値を抽出する他の関数。私もそれを見逃しているようです。 だから、私はで即興演奏しましたが、それは自明でないケースは失敗します...json_array_elements(json)texttextJSON
trim()


いつものように良い投稿ですが、内部についての知識があるので、なぜarray-> jsonbからキャストされないのですか。sql-arrayがより強く型付けされているため、他のキャストを実装しないことを理解できます。PostgreSQLがキャストするコード(int []、bigint []、text [])を自動生成することを嫌っているというだけの理由ですか?
エヴァンキャロル

3
@Evan:to_jsonb()配列-> jsonb変換に使用します。
アーウィンブランドステッター

SELECT ARRAY(SELECT json_array_elements_text(_js))本当に配列の順序が保存されていることを保証しますか?オプティマイザーは、json_array_elements_textから出力される行の順序を理論的に変更することを許可されていませんか?
フェリックスガイゼンデルファー

@Felix:SQL標準には正式な保証はありません。(繰り返しますが、標準のSQLのSELECTリストでは、セットを返す関数は最初から許可されていません。)しかし、Postgresマニュアルには非公式のアサーションがあります。参照:dba.stackexchange.com/a/185862/3684は、明示的であるために-マイナーもパフォーマンスペナルティの費用で-参照: dba.stackexchange.com/a/27287/3684を。個人的には、この特定の表現は、9.4以降のすべての現在および将来のPostgresバージョンで期待どおりに機能することを100%確信しています。
アーウィンブランドステッター

@ErwinBrandstetter、これを確認してくれてありがとう!現在、PostgreSQLが提供する注文保証の形式的および非公式の保証をまとめた記事の調査を行っていますが、その答えは非常に役に立ちました。記事を確認したい場合はお知らせください。そうでない場合は心配ありません。StackOverflowの貢献に非常に感謝しており、長年にわたって多くのことを学びました!
フェリックスガイゼンデルファー

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

回答にもっと高速な式を追加し、より簡単な関数を追加しました。これは大幅に安くなります。
アーウィンブランドステッター

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
 

基本的に、値を単一要素の配列に変換し、最初の要素を要求します。

別のアプローチは、この演算子を使用してすべてのフィールドを1つずつ抽出することです。しかし、大きな配列の場合、各配列要素の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 16

TRANSLATE(jsonb :: jsonb :: text、 '[]'、 '{}'):: INT []はjson配列をpg配列に変換します。
FiscalCliff 16

SELECT translate('["foo", "bar"]'::jsonb::text, '[]','{}')::INT[]; ERROR: invalid input syntax for integer: "foo"それは...そう爆弾プルーフではありません
dezso

[]これらのアレイのためのテキストを使用することを検討
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.