我在 PostgreSQL 9.6 中有一个 jsonb 结构,它包含一个类似于下面示例的嵌套数组结构:
continents:[
{
id: 1,
name: 'North America',
countries: [
{
id: 1,
name: 'USA',
subdivision: [
{
id: 1,
name: 'Oregon',
type: 'SOME_TYPE'
}
]
}
]
}
]
如何更改多个分割的“类型”属性,因为它嵌套在两个数组(国家/地区 和分割)中?
我遇到了其他答案,并且能够逐条记录地这样做(假设表是 map 并且 jsonb 列是 divisions ):
update map
set divisions = jsonb_set( divisions, '{continents,0,countries,0,subdivisions,0,type}', '"STATE"', FALSE);
有没有办法以编程方式更改所有分割的该属性?
我想我已经接近了,我可以使用下面的查询查询所有分割类型,但我正在努力弄清楚如何更新它们:
WITH subdivision_data AS (
WITH country_data AS (
select continents -> 'countries' as countries
from map, jsonb_array_elements( map.divisions -> 'continents' ) continents
)
select country_item -> 'subdivisions' as subdivisions
from country_data cd, jsonb_array_elements( cd.countries ) country_item
)
select subdivision_item ->> 'type' as subdivision_type
from subdivision_data sub, jsonb_array_elements( sub.subdivisions ) subdivision_item;
以下是我遇到的一些问题。它们似乎只有在您尝试更新单级数组时才有效:
postgresql 9.5 using jsonb_set for updating specific jsonb array value
How to update deeply nested JSON object based on filter criteria in Postgres?
最佳答案
起初我认为这样的事情会起作用:
update map as m set
divisions = jsonb_set(m1.divisions, array['continents',(d.rn-1)::text,'countries',(c.rn-1)::text,'subdivisions',(s.rn-1)::text,'type'], '"STATE"', FALSE)
from map as m1,
jsonb_array_elements(m1.divisions -> 'continents') with ordinality as d(data,rn),
jsonb_array_elements(d.data -> 'countries') with ordinality as c(data,rn),
jsonb_array_elements(c.data -> 'subdivisions') with ordinality as s(data,rn)
where
m1.id = m.id
但这不起作用 - 请参阅 documentation
When a FROM clause is present, what essentially happens is that the target table is joined to the tables mentioned in the from_list, and each output row of the join represents an update operation for the target table. When using FROM you should ensure that the join produces at most one output row for each row to be modified. In other words, a target row shouldn't join to more than one row from the other table(s). If it does, then only one of the join rows will be used to update the target row, but which one will be used is not readily predictable.
你可以做的是用 functions-json
取消嵌套你的 jsons然后将它们聚合回来:
update map set
divisions = jsonb_set(divisions, array['continents'],
(select
jsonb_agg(jsonb_set(
d, array['countries'],
(select
jsonb_agg(jsonb_set(
c, array['subdivisions'],
(select
jsonb_agg(jsonb_set(s, array['type'], '"STATE"', FALSE))
from jsonb_array_elements(c -> 'subdivisions') as s),
FALSE
))
from jsonb_array_elements(d -> 'countries') as c)
))
from jsonb_array_elements(divisions -> 'continents') as d),
FALSE
)
您还可以创建可以用来代替多个子查询的辅助函数:
create function jsonb_update_path(_data jsonb, _path text[], _value jsonb)
returns jsonb
as $$
begin
if array_length(_path, 1) = 1 then
return jsonb_set(_data, _path, _value, FALSE);
else
return (
jsonb_set(
_data, _path[1:1],
(
select
jsonb_agg(jsonb_update_path(e, _path[2:], _value))
from jsonb_array_elements(_data -> _path[1]) as e
)
)
);
end if;
end
$$
language plpgsql
update map set
divisions = jsonb_update_path(divisions, '{continents,countries,subdivisions,type}', '"STATE"')
关于arrays - PostgreSQL:更新 JSONB 结构内嵌套数组中元素的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55697028/