71

I have DB table with jsonb column.

number  | data
    1   | {"name": "firstName", "city": "toronto", "province": "ON"}

I need a way to update data column. So my output should look like:

{"name": "firstName", "city": "ottawa", "province": "ON", "phone": "phonenum", "prefix": "prefixedName"}

Is it possible with json_set? I have added query like:

update table_name set data = jsonb_set(data, '{city}', '"ottawa"') where number = 1;

However, I need a way to add new key-value if it does not exists and update key value if it exists. Is it possible to achieve this in single query?

Barbaros Özhan
  • 59,113
  • 10
  • 31
  • 55
stack_d_code
  • 1,196
  • 2
  • 12
  • 19

2 Answers2

165

The documentation says:

The || operator concatenates the elements at the top level of each of its operands. ... For example, if both operands are objects with a common key field name, the value of the field in the result will just be the value from the right hand operand.

So using your example data:

update table_name set
    data = data || '{"city": "ottawa", "phone": "phonenum", "prefix": "prefixedName"}'
    where number = 1;

Additionally if the object you want to edit is not at the top level - just combine the concatenation and jsonb_set function. For example, if the original data looks like

{"location": {"name": "firstName", "city": "toronto", "province": "ON"}}

then

...
data = jsonb_set(
    data, 
    '{location}', data->'location' || '{"city": "ottawa", "phone": "phonenum", "prefix": "prefixedName"}')
...
Abelisto
  • 14,826
  • 2
  • 33
  • 41
  • 3
    Awesome, this works like charm. Thanks! I am not able to up-vote it just yet because I dont have enough points, but definitely thumbs up. – stack_d_code Aug 11 '16 at 13:05
  • 12
    @MarcoPrins Have you tried to read the Oracle documentation? Compared to it the PostgreSQL documentation is a literary masterpiece :o) – Abelisto May 02 '17 at 06:58
  • sometimes when you get the error : unknown signature: jsonb_set(jsonb, string, jsonb) it is due to typecasting. Adding ::TEXT[] as shown below solved the issue in my case ... data = jsonb_set(data, '{location}' ::TEXT[] , data->'location' || '{"city": "ottawa", "phone": "phonenum", "prefix": "prefixedName"}' ::jsonb) ... – U R Nov 01 '19 at 13:15
  • 9
    Helpful hint if you are populating the updated fields from dynamic data within your query, and/or updating multiple fields.. You can use `json_build_object` to create the update object. For example: `data->'location' || jsonb_build_object('city', "cityColName", 'phonenum', "phoneCalName")`. This is much easier than trying to dynamically creating jsonb strings as @Paarth suggests below. – mmigdol Jan 17 '20 at 02:00
  • I am not able use JPA Query to update multiple jsonb column fields, any help ? – dynamo Sep 16 '21 at 10:18
  • I am getting ERROR: operator does not exist: character varying -> unknown Hint: No operator matches the given name and argument type(s). How can it be resolved? – CoderBeginner Oct 09 '22 at 07:02
1

You can try this

Here we are using jsonb concatation operator || to Concatenate two jsonb objects

update table_name set data = (select val from (
(select 
CASE WHEN data ? key THEN jsonb_set(data, '{' || key || '}', quote_nullable(updated_value))
ELSE 
data || ('{' || quote_ident(key) || ':' || quote_ident(some_value) || '}')::jsonb
END val
 from json_each_text((select data::json from tbl))
CROSS JOIN tbl t
where key in ('city','phone','prefix') and number=1)) where number=1
Paarth
  • 580
  • 3
  • 10
  • 1
    please refer [jsonb in postgres] (https://www.postgresql.org/docs/9.6/static/functions-json.html) – Paarth Aug 11 '16 at 08:12