[英]Delete element in a deeply nested array in jsonb column - Postgres
我有一个表my_table
,其中包含一些包含一些数据的jsonb列,例如,在一行中,该列可以包含以下数据:
[
{
"x_id": "1",
"type": "t1",
"parts": [
{ "part_id": "1", price: 400 },
{ "part_id": "2", price: 500 },
{ "part_id": "3", price: 0 }
]
},
{
"x_id": "2",
"type": "t1",
"parts": [
{ "part_id": "1", price: 1000 },
{ "part_id": "3", price: 60 }
]
},
{
"x_id": "3",
"type": "t2",
"parts": [
{ "part_id": "1", price: 100 },
{ "part_id": "3", price: 780 },
{ "part_id": "2", price: 990 }
]
}
]
我需要帮助找到如何在给定x_id
和part_id
从parts
数组中删除元素。
例
给定x_id=2
和part_id=1
,我需要将数据更新为:
[
{
"x_id": "1",
"type": "t1",
"parts": [
{ "part_id": "1", price: 400 },
{ "part_id": "2", price: 500 },
{ "part_id": "3", price: 0 }
]
},
{
"x_id": "2",
"type": "t1",
"parts": [
{ "part_id": "3", price: 60 }
]
},
{
"x_id": "3",
"type": "t2",
"parts": [
{ "part_id": "1", price: 100 },
{ "part_id": "3", price: 780 },
{ "part_id": "2", price: 990 }
]
}
]
PS1:这些数据无法规范化,因此这不是一个可能的解决方案。
PS2:我正在运行PostgreSQL 9.6
PS3:我已经检查了这个问题和这个问题但是我的数据结构与其他问题相比似乎过于复杂,因此我不能应用给定的答案。
Edit1 :json数据可能很大,尤其是parts
数组,它可以有0个元素到数千个元素。
这应该工作,只需要另一个唯一的列(通常是主键)
创建测试表
create table test_tab(
id serial primary key,
j jsonb
);
insert into test_tab
(j)
values
('[
{
"x_id": "1",
"type": "t1",
"parts": [
{ "part_id": "1", "price": 400 },
{ "part_id": "2", "price": 500 },
{ "part_id": "3", "price": 0 }
]
},
{
"x_id": "2",
"type": "t1",
"parts": [
{ "part_id": "1", "price": 1000 },
{ "part_id": "3", "price": 60 }
]
},
{
"x_id": "3",
"type": "t2",
"parts": [
{ "part_id": "1", "price": 100 },
{ "part_id": "3", "price": 780 },
{ "part_id": "2", "price": 990 }
]
}
]');
然后拆分json,过滤不必要的数据,并再次重新创建json:
select id, jsonb_agg( jsonb_build_object('x_id',xid, 'type',type, 'parts', case when inner_arr = '[null]'::jsonb then parts_arr::jsonb else inner_arr end) )
from (
select
id,
value->>'x_id' as xid,
jsonb_agg(inner_arr) as inner_arr,
max(value->>'parts') as parts_arr,
max(value->>'type') as type
from (
select * ,
case when value->>'x_id'='2' then jsonb_array_elements(value->'parts') else NULL end inner_arr
from test_tab
join lateral jsonb_array_elements(j)
on true
) t
where
inner_arr->>'part_id' is distinct from '1'
group by id, value->>'x_id'
) t
group by id
我认为你可以使用#-
operator(参见functions-json
),你只需要找到从中删除数组元素的路径:
select
data #- p.path
from test as t
cross join lateral (
select array[(a.i-1)::text,'parts',(b.i-1)::text]
from jsonb_array_elements(t.data) with ordinality as a(data,i),
jsonb_array_elements(a.data->'parts') with ordinality as b(data,i)
where
a.data ->> 'x_id' = '2' and
b.data ->> 'part_id' = '1'
) as p(path)
要么
update test as t set
data = data #- (
select
array[(a.i-1)::text,'parts',(b.i-1)::text]
from jsonb_array_elements(t.data) with ordinality as a(data,i),
jsonb_array_elements(a.data->'parts') with ordinality as b(data,i)
where
a.data ->> 'x_id' = '2' and
b.data ->> 'part_id' = '1'
)
更新好的,如果数据中不存在给定路径,则有合理的评论认为更新部分工作不正确。 我想在这种情况下你要在where子句中复制表达式:
update test as t set
data = data #- (
select
array[(a.i-1)::text,'parts',(b.i-1)::text]
from jsonb_array_elements(t.data) with ordinality as a(data,i),
jsonb_array_elements(a.data->'parts') with ordinality as b(data,i)
where
a.data ->> 'x_id' = '2' and
b.data ->> 'part_id' = '23222'
)
where
exists (
select *
from jsonb_array_elements(t.data) as a(data),
jsonb_array_elements(a.data->'parts') as b(data)
where
a.data ->> 'x_id' = '2' and
b.data ->> 'part_id' = '23222'
)
或者你可以使用自我加入:
update test as t2 set
data = t.data #- p.path
from test as t
cross join lateral (
select array[(a.i-1)::text,'parts',(b.i-1)::text]
from jsonb_array_elements(t.data) with ordinality as a(data,i),
jsonb_array_elements(a.data->'parts') with ordinality as b(data,i)
where
a.data ->> 'x_id' = '2' and
b.data ->> 'part_id' = '23232'
) as p(path)
where
t.ctid = t2.ctid
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.