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

时间:2016-08-30 20:51:51

标签: arrays json postgresql jsonb

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

  

无法从标量中提取元素

我认为这是因为返回调用中的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。 ..],...}

2 个答案:

答案 0 :(得分:11)

重要提示:事情已从Postgres 10及以上更改,因此请根据您的数据库版本找到正确的解决方案。改变了什么?从Postgres 10开始的CASE语句中不允许使用设置返回函数,jsonb_array_elements就是这样的函数。

10之前的Postgres版本

在您的数据中,必须有一些标量值,而不是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设置返回函数,我们需要编写更多代码来实现相同的功能。设置返回函数意味着函数调用可以输出多行,并且不允许在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

第二步是使用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

答案 1 :(得分:2)

经过研究,我发现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;