问题
Trying to update all elements of a nested array in a jsonb
column, but only one element is updated. My query:
update table_
set value_ = jsonb_set(value_,cte.json_path,cte.namevalue,false) FROM (
select
vals2->'ao'->'sc'->'name' as namevalue,
('{iProps,'||index1-1||',value,rules,'||index2-1||',ao,sc}')::text[] as json_path
from
table_,
jsonb_array_elements(value_->'iProps')
with ordinality arr1(vals1,index1),
jsonb_array_elements(vals1->'value'->'rules')
with ordinality arr2(vals2,index2)
) AS cte;
See demo with sample value:
db<>fiddle here
Am unable to understand why this query updates the first object in the rules
array:
iProps -> value -> rules -> ao -> sc -> name = "name1"
But not the subsequent ones:
iProps -> value -> rules -> ao -> sc -> name = "name2"
iProps -> value -> rules -> ao -> sc -> name = "name3"
回答1:
Explanation
The subselect in the FROM
clause of your UPDATE
returns three rows. But every row in the target table can only be updated once in a single UPDATE
command. The result is that you only see the effect of one of those three rows.
Or, in the words of the manual:
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.
Aside: don't call your subquery "cte". It's not a Common Table Expression.
Proper UPDATE
UPDATE table_ t
SET value_ = jsonb_set(value_, '{iProps}', sub2.new_prop, false)
FROM (
SELECT id
, jsonb_agg(jsonb_set(prop, '{value, rules}', new_rules, false)
ORDER BY idx1) AS new_prop
FROM (
SELECT t.id, arr1.prop, arr1.idx1
, jsonb_agg(jsonb_set(rule, '{ao,sc}', rule #> '{ao,sc,name}', false)
ORDER BY idx2) AS new_rules
FROM table_ t
, jsonb_array_elements(value_->'iProps') WITH ORDINALITY arr1(prop,idx1)
, jsonb_array_elements(prop->'value'->'rules') WITH ORDINALITY arr2(rule,idx2)
GROUP BY t.id, arr1.prop, arr1.idx1
) sub1
GROUP BY id
) sub2
WHERE t.id = sub2.id;
db<>fiddle here
Use jsonb_set()
on each object (array element) before aggregating them back into an array. First at the leaf level, and again on the deeper level.
I added id
as PRIMARY KEY
to the table. We need some unique column to keep rows separate.
The added ORDER BY
may or may not be required. Added it to guarantee original order.
Of course, if your data is as regular as the sample, a relational design with dedicated columns might be a simpler alternative. See
- How to perform update operations on columns of type JSONB in Postgres 9.4
来源:https://stackoverflow.com/questions/65799161/update-with-jsonb-set-only-affects-one-object-in-nested-array