我正在尝试在Postgres中构建一个支持数组和对象的递归CTE,以返回一个键值对列表,并且似乎无法找到一个好例子。这是我目前的代码。
SELECT *
FROM table
WHERE id >= (SELECT MAX(id) FROM table) - 10
ORDER BY id DESC
正如您所看到的,只要我按下数组而不是对象,代码就会停止递归。我尝试过使用case语句并将函数调用放在case语句中的jsonb_each或jsonb_array_element,但我收到错误告诉我改为使用横向连接。
答案 0 :(得分:1)
我使用此示例表使查询更具可读性:
create table my_table(id serial primary key, jdata jsonb);
insert into my_table (jdata) values
('{
"key1": {
"key2": [
{
"key3": "test3",
"key4": "test4"
}
]
},
"key5": [
{
"key6":
[
{
"key7": "test7"
}
]
}
]
}');
您必须有条件地加入jsonb_each(value)
和jsonb_array_elements(value)
,具体取决于value
的类型:
with recursive extract_all as
(
select
key as path,
value
from my_table
cross join lateral jsonb_each(jdata)
union all
select
path || '.' || coalesce(obj_key, (arr_key- 1)::text),
coalesce(obj_value, arr_value)
from extract_all
left join lateral
jsonb_each(case jsonb_typeof(value) when 'object' then value end)
as o(obj_key, obj_value)
on jsonb_typeof(value) = 'object'
left join lateral
jsonb_array_elements(case jsonb_typeof(value) when 'array' then value end)
with ordinality as a(arr_value, arr_key)
on jsonb_typeof(value) = 'array'
where obj_key is not null or arr_key is not null
)
select *
from extract_all;
输出:
path | value
--------------------+------------------------------------------------
key1 | {"key2": [{"key3": "test3", "key4": "test4"}]}
key5 | [{"key6": [{"key7": "test7"}]}]
key1.key2 | [{"key3": "test3", "key4": "test4"}]
key5.0 | {"key6": [{"key7": "test7"}]}
key1.key2.0 | {"key3": "test3", "key4": "test4"}
key5.0.key6 | [{"key7": "test7"}]
key1.key2.0.key3 | "test3"
key1.key2.0.key4 | "test4"
key5.0.key6.0 | {"key7": "test7"}
key5.0.key6.0.key7 | "test7"
(10 rows)
json数组的元素没有键,我们应该使用它们的索引来构建路径。因此,应该用普通性调用函数jsonb_array_elements()
。每the documentation(参见 7.2.1.4。表函数):
如果指定了WITH ORDINALITY子句,则会在函数结果列中添加bigint类型的附加列。此列从1开始对函数结果集的行进行编号。
函数调用
jsonb_array_elements(case jsonb_typeof(value) when 'array' then value end)
with ordinality as a(arr_value, arr_key)
将对象(value, ordinality)
别名为(arr_value, arr_key)
。