问题
I have a table named guest_group
in a Postgres DB and a column named custom_fields
with the following values: (example)
[
{
"value":"Frau Barbara Test",
"display_name":"Name",
"servicio_tags":[
"full-name"
]
},
{
"value":"118",
"display_name":"Zimmernummer",
"servicio_tags":[
"room-number"
]
},
{
"value":"likes postgreSQL",
"display_name":"Traces",
"servicio_tags":[
"trace"
]
}
]
I want to update all rows with the
value
equals toFrau Barbara Test
and where theservicio_tags
array is equals to["full-name"]
- plus where the
value
equal to118
where theservicio_tags
array is equal to["room-number"]
The Update should add a new object to the JSONB:
{
"value":"likes JSONB",
"display_name":"Traces",
"servicio_tags":[
"trace"
]
}
My try so far:
UPDATE guest_group
SET custom_fields =
jsonb_insert('[{"value": "Frau Barbara Test", "display_name": "Name", "servicio_tags": ["full-name"]}, {"value": "118", "display_name": "Zimmernummer", "servicio_tags": ["room-number"]}]'::jsonb,
'{0}',
'{"value": "likes JSONB", "display_name": "Traces", "servicio_tags": ["trace"]}'::jsonb,
true)
where custom_fields::text like '%"Frau Barbara Test"%'
and custom_fields::text like '%"118 "%'
This try changes the value in column of the specific row to the following:
[
{
"value":"Frau Barbara Test",
"display_name":"Name",
"servicio_tags":[
"full-name"
]
},
{
"value":"likes JSONB",
"display_name":"Traces",
"servicio_tags":[
"trace"
]
},
{
"value":"118",
"display_name":"Zimmernummer",
"servicio_tags":[
"room-number"
]
}
]
So the:
{
"value":"likes postgreSQL",
"display_name":"Traces",
"servicio_tags":[
"trace"
]
}
gets lost. How can do the update without loosing this data?
回答1:
I would recommend not to compare JSON as strings. Instead, use the vast array of JSON operators and functions. To find rows that contain Mrs. Test in a value, you could for example use the @>
operator:
WHERE '[{"value": "Frau Barbara Test"}]'::jsonb <@ custom_fields
See also Postgresql query array of objects in JSONB field.
However, this doesn't yet check that it's the same object that has a full-name
tag, so we might try
WHERE '[{"value": "Frau Barbara Test", "servicio_tags": ["full-name"]}]'::jsonb <@ custom_fields
That's much better than text comparison, but still not exactly what you asked for. Due to the semantics of the @>
operator, it checks the tags array to contain a "full-name"
string, not to have it as its single element.
To do that, it gets a bit more complicated, involving a subquery on the jsonb_array_elements
and accessing its json components explicitly for comparison:
SELECT *
FROM guest_group
WHERE EXISTS(
SELECT 1
FROM jsonb_array_elements(custom_fields) AS el
WHERE el->>'value' = 'Frau Barbara Test'
AND el->'servicio_tags' = '["full-name"]'::jsonb)
You could also mix the two approaches using ANY
:
SELECT *
FROM guest_group
WHERE '{"value": "Frau Barbara Test"}' <@ ANY(
SELECT el
FROM jsonb_array_elements(custom_fields) AS el
WHERE el->'servicio_tags' = '["full-name"]'::jsonb)
(online demo)
Now equipped with these tools, you can add a second condition to match the room number. Then use
UPDATE guest_group
SET custom_fields = jsonb_insert(custom_fields, '{0}', '{"value": "likes JSONB", "display_name": "Traces", "servicio_tags": ["trace"]}'::jsonb, true)
WHERE …
回答2:
This seems to work:
UPDATE guest_group
SET custom_fields =
jsonb_insert(custom_fields::jsonb,'{0}','{"value": "likes JSONB", "display_name": "Traces", "servicio_tags": ["trace"]}'::jsonb)
WHERE custom_fields::text like '%"Frau Barbara Test"%'
AND custom_fields::text like '%"117"%'
来源:https://stackoverflow.com/questions/58671679/update-jsonb-column-where-jsonb-column-has-certain-value-postgresql