Extract json array from postgres table gives error: cannot extract elements from a scalar
Extract json array from postgres table gives error: cannot extract elements from a scalar
通过使用 jsonb_array_elements()
函数从 Postgres 中提取出 jsonb
数据数组,它给出了错误:
cannot extract elements from a scalar
我假设这是因为 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...], ...}
重要说明: 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
子句中的函数调用从它 select 像这样:
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 中的一个更改打破了原来的答案。
下面是我在 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;
通过使用 jsonb_array_elements()
函数从 Postgres 中提取出 jsonb
数据数组,它给出了错误:
cannot extract elements from a scalar
我假设这是因为 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...], ...}
重要说明: 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
子句中的函数调用从它 select 像这样:
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 中的一个更改打破了原来的答案。
下面是我在 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;