从postgres表中提取json数组会产生错误:无法从标量中提取元素

Hai*_* Qu 9 arrays postgresql json jsonb

通过使用jsonb_array_elements()函数jsonb从Postgres中提取数据数组,它给出了错误:

不能从标量中提取元素

我认为这是因为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
Run Code Online (Sandbox Code Playgroud)

截断的json数组看起来像:

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

Kam*_*ski 15

重要说明:事情已从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);
Run Code Online (Sandbox Code Playgroud)

结果

 date
------
 123
 456
Run Code Online (Sandbox Code Playgroud)

因此,您的查询需要像这样编写来处理这种情况:

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
Run Code Online (Sandbox Code Playgroud)

Postgres版本10+

由于不允许从Pg10设置返回函数,我们需要编写更多代码来实现相同的功能.设置返回函数意味着函数调用可以输出多行,并且不允许在CASE语句中使用.简单地说,Postgres希望我们为此编写显式代码.

逻辑保持与上面相同(参考10之前的pg版本),但我们将分两步而不是一步.

首先,我们需要找到两种类型的通用表示:数字和数组.我们可以从一个数字中创建一个数组,因此数组将是一个不错的选择.我们所做的是为每个案例构建一个数组(阅读评论):

  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
Run Code Online (Sandbox Code Playgroud)

第二步是将我们的数据类型转换包装在一个语句using WITH子句中,然后在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
Run Code Online (Sandbox Code Playgroud)