从 postgres 表中提取 json 数组给出错误:无法从标量中提取元素

Posted

技术标签:

【中文标题】从 postgres 表中提取 json 数组给出错误:无法从标量中提取元素【英文标题】:Extract json array from postgres table gives error: cannot extract elements from a scalar 【发布时间】:2017-01-07 07:02:42 【问题描述】:

使用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...], ...

【问题讨论】:

请提供您的示例 json 值。错误似乎很明显。您传递给 jsonb_array_elements 的值是标量,而不是 json 数组。 "stats_by_date": "date": [16632, 16633, 16634, 16635, 16636, 16637, 16638, 16639, 16640, 16641, 16642, 16643, 16644, 166645, 166645, 16648,16649,16650,16651,16652,16653,16654,16655,16656,16657,16658,16659,16660,16661,16662,16663,16664,16665,16666,16667,16668,16669,16670,16671,16672, 16673, 16674], "imps": [2418, 896, 1005...], ... 它适用于限制多个条目,如果在所有数据表上循环,则会出现错误,因此可能有一些空值/标量传递给jsonb_array_elements 函数。想知道如何检查和绕过它 【参考方案1】:

重要提示:从 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版本),但是我们将分两步完成。

首先,我们需要找到两种类型的共同表示:数字和数组。我们可以用一个数字组成一个数组,所以数组是一个不错的选择。我们所做的是为每种情况构建一个数组(读取 cmets):

  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

【讨论】:

在针对 PostgreSQL 10.3 运行示例时,我收到错误消息“错误:CASE 中不允许设置返回函数”。我读过的所有内容听起来都应该可行,但我失败了。 @TurboGus 感谢您提供的信息。我将更新帖子,因为从 Postgres 10 开始不允许设置返回函数。同时检查:tapoueh.org/blog/2017/10/… @TurboGus 我已经调整了 Postgres 10+ 版本的答案。再次感谢您的指出,并对迟到的回复感到抱歉。【参考方案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;

【讨论】:

以上是关于从 postgres 表中提取 json 数组给出错误:无法从标量中提取元素的主要内容,如果未能解决你的问题,请参考以下文章

如何从 MySQL 中提取 JSON 数组

从spark中的json中的数组中提取Json

从 BigQuery 中的 JSON 数组中提取多个值

从 Redshift 表中获取 JSON 数据

使用 Laravel Blade 从 Postgres 显示 JSON 数据

在Jmeter的数组中提取json