web-dev-qa-db-ja.com

未定義のJSONBフィールドを動的に行に変換するPostgreSQL

タイプjsonbのフィールドを持つテーブルがあります。すべてのjsonキーに対してフィールドjsonbの列を分割したいと思います。この列にはスキーマがありません。例えば:

から

CREATE TABLE v(id,jsonb)
  AS VALUES
    (1,'{"a":"4", "b":"5"}'::jsonb),
    (2,'{}'),
    (3,'{"a":"8", "c":"9", "d":"9"}');

id  |  a   |   b   |  c  |  d

1   |  4   |   5   |     |  
3   |  8   |       |  9  |  9

この特定のケースでは、1つの解決策は

select * from table, json_to_record(optional) as x("a" text, "b" text, "c" text, d text)

ご覧のとおり、キーは変化する可能性があり、大きなデータベースではすべてのキーを実際の問題に配置することが困難です。31個のキーがありますが、他のテーブルでこのスクリプトを再利用したい場合は、手動でキーを入力する必要があります。

キーを手動で指定せずにjsonbのキー全体を選択する方法はありますか?

5
Cyberguille

私の質問:キーを手動で指定せずにjsonbのキー全体を選択する方法はありますか?

いいえ、クエリが未定義の結果セットを返す方法はありません。ただし、テーブルが新しいクエリを受け入れない場合は、動的SQLステートメントを生成できます。

SELECT FORMAT(
  $$ SELECT * FROM %I.%I CROSS JOIN LATERAL jsonb_to_record(%I) AS rs(%s); $$,
  'public',
  'v',
  'jsonb',
  array_to_string(
                (SELECT ARRAY(SELECT DISTINCT col FROM v CROSS JOIN LATERAL jsonb_object_keys(jsonb) AS t(col) ORDER BY col)), ' text , '
  ) || ' text'
);

次に、そのクエリを実行するか、psqlで\gexecを実行します。

                                                    format                                                    
--------------------------------------------------------------------------------------------------------------
  SELECT * FROM public.v CROSS JOIN LATERAL jsonb_to_record(jsonb) AS rs(a text , b text , c text , d text); 
(1 row)

test=# \gexec
 id |             jsonb              | a | b | c | d 
----+--------------------------------+---+---+---+---
  1 | {"a": "4", "b": "5"}           | 4 | 5 |   | 
  2 | {}                             |   |   |   | 
  3 | {"a": "8", "c": "9", "d": "9"} | 8 |   | 9 | 9
(3 rows)

jsonb_typeofを使用して型推論を構築してpg型に戻したい場合も、そうでない場合もあります。整数などに戻ることはできませんが、double precisionとして数値を格納できるはずです。

1
Evan Carroll