web-dev-qa-db-ja.com

postgresテーブルからjson配列を抽出するとエラーが発生する:スカラーから要素を抽出できません

Postgresからjsonb_array_elements()関数を使用してjsonbデータ配列を抽出すると、エラーが発生しました。

スカラーから要素を抽出できません

これは、return呼び出しのNULLが原因であると想定し、NULLチェック条件を追加しましたが、機能しません。助けてくれてありがとう。

   select id ,
   CASE
    WHEN report IS NULL OR 
         (report->'stats_by_date') IS NULL OR 
         (report->'stats_by_date'-> 'date') IS NULL then to_json(0)::jsonb
    ELSE jsonb_array_elements(report -> 'stats_by_date' -> 'date') 
    END AS Date
   from factor_reports_table

切り捨てられたjson配列は次のようになります。

"stats_by_date":{"date":[16632、16633、16634、...]、 "imps":[2418、896、1005 ...]、...}

10
Hai Qu

重要な注意: Postgres 10以降から変更されたので、データベースのバージョンに応じて適切なソリューションに進んでください。何が変わったの?セットを返す関数は、Postgres 10以降のCASEステートメントで使用できません。_jsonb_array_elements_はそのような関数です。

Postgresバージョン10以前

データには、dateキー内の配列ではなく、スカラー値が必要です。

jsonb_typeof()を使用して、どのタイプが特定のキーであるかを識別し、CASEステートメント内でラップすることができます。

以下の入力セットとしてのスカラーと配列の例を検討してください。

_select 
  case when jsonb_typeof(jsonb_column->'stats_by_date'->'date') = 'array' 
       then jsonb_array_elements(jsonb_column->'stats_by_date'->'date') 
       else jsonb_column->'stats_by_date'->'date' 
  end as date
from (
  select '{"stats_by_date": {"date": 123}}'::jsonb -- scalar (type: 'number')
  union all 
  select '{"stats_by_date": {"date": [456]}}'::jsonb -- array (type: 'array')
  ) foo(jsonb_column);
_

結果

_ date
------
 123
 456
_

そのため、このようなケースを処理するには、クエリを次のように記述する必要があります。

_select id,
  case when jsonb_typeof(jsonb_column->'stats_by_date'->'date') = 'array' 
       then jsonb_array_elements(jsonb_column->'stats_by_date'->'date') 
       else jsonb_column->'stats_by_date'->'date' 
  end as date
from factor_reports_table
_

Postgresバージョン10以降

Pg10ではセットを返す関数は許可されていないため、同じことを実現するためにもう少しコードを書く必要があります。 Set return functionは、関数呼び出しが複数の行を出力でき、CASEステートメントでの使用が禁止されていることを意味します。 簡単に言えば、Postgresはこのための明示的なコードを書くことを望んでいます。

ロジックは上記と同じです(10より前のpgバージョンを参照)が、1ステップではなく2ステップで実行します。

まず、数値と配列の両方のタイプの共通表現を見つける必要があります。 1つの数値から配列を作成できるので、配列を選択することをお勧めします。私たちがしていることは、すべてのケースの配列を作成することです(コメントを読む):

_  case when jsonb_typeof(jsonb_column->'stats_by_date'->'date') = 'array' -- if array
       then jsonb_column->'stats_by_date'->'date' -- leave it as it is
       else jsonb_build_array(jsonb_column->'stats_by_date'->'date') -- if not array, build array
  end as date
_

2番目のステップは、WITH句を使用して1つのステートメント内でデータ型変換をラップし、次のようにFROM句の関数呼び出しを使用してそこから選択することです。

_with json_arrays as (
select 
  case when jsonb_typeof(jsonb_column->'stats_by_date'->'date') = 'array' 
       then jsonb_column->'stats_by_date'->'date'
       else jsonb_build_array(jsonb_column->'stats_by_date'->'date')
  end as date
from (
  select '{"stats_by_date": {"date": 123}}'::jsonb -- scalar (type: 'number')
  union all 
  select '{"stats_by_date": {"date": [456]}}'::jsonb -- array (type: 'array')
  ) foo(jsonb_column)
)
select t.date
from 
  json_arrays j -- this is refering to our named WITH clause
, jsonb_array_elements(date) t(date) -- call function to get array elements
_
17

調査の結果、PostgreSQL 10で元の回答に反する変更があったことがわかりました。

これが10の例です。

select jsonb_array_elements(test.date) as date
from
(select
  case when jsonb_typeof(jsonb_column->'stats_by_date'->'date') = 'array'
       then jsonb_column->'stats_by_date'->'date'
       else jsonb_build_array(jsonb_column->'stats_by_date'->'date')
  end as date
from (
  select '{"stats_by_date": {"date": 123}}'::jsonb -- scalar (type: 'number')
  union all
  select '{"stats_by_date": {"date": [456]}}'::jsonb -- array (type: 'array')
  ) foo(jsonb_column)) as test;
2
TurboGus