sqljsonpostgresqljson.netsupabase

Update table according with JSON/JSONB


I have a table books :

Name of column Type
id int (PK)
name text
nb_pages int

And a table book_modifications :

Name of column Type
id int (PK)
old_data jsonb
new_data jsonb
book_id int (FK)

Now let's imagine I have a row in my books table like this :

id: 15,
name: "name-1",
nb_pages: 120

And a row to insert in my book_modifications table like this :

id: 7,
old_data: {name: "name-1", nb_pages: 120},
new_data: {name: "new-name-1"},
book_id: 15

My question is :

How can I update my books table with the new_data column of my book_modifications table in SQL or PostgreSQL ?

Currently I have found this way :

create or replace function on_book_modifications_insert () 
returns trigger 
language plpgsql 
as $$
begin

-- Here (new_data only contains the 'name' field)
UPDATE books 
SET name = new.new_data -> 'name', nb_pages = new.new_data -> 'nb_pages' 
WHERE id = new.book_id;

return new;

end;
$$

In this case, the SQL query above will update nb_pages to null because it is not present in new_data (which will generate an error), whereas I would like to leave nb_pages at 120 and only update fields present in new_data.

For INSERT, I've seen that json_object exists, but I can't find it for UPDATE.


Solution

  • You can just use COALESCE against the original value. If the JSONB value doesn't exist then you get null, which is replaced with the original, so effectively a no-op.

    create or replace function on_book_modifications () 
    returns trigger 
    language plpgsql 
    as $$
    begin
    
    -- Here (new_data only contains the 'name' field)
    UPDATE books
    SET
      name = COALESCE(new.new_data ->> 'name', books.name),
      nb_pages = COALESCE((new.new_data ->> 'nb_pages')::int, books.nb_pages)
    WHERE books.id = new.book_id;
    
    return new;
    
    end;
    $$
    

    db<>fiddle