Reputation: 55
I'm learning about Postgresql - I don't have a specific application in mind, but the general idea is to update one column with json and not worry about the rest.
I have a table with a few columns (id, name, amount, json). I would like to update only the json column and have a trigger update the other columns from the json.
For example, if json was set to {"name" = "fred", "amount" = 100}, the trigger would populate the name and amount columns
so far, this works:
create table one(id serial primary key, name text, amount int, data jsonb);
CREATE OR REPLACE FUNCTION test()
RETURNS trigger AS
$$
DECLARE
json_name text = (data -> 'name') from one;
json_amount int = (data -> 'amount') from one;
BEGIN
IF json_name IS DISTINCT FROM OLD.name THEN
UPDATE one SET name = json_name;
END IF;
IF json_amount IS DISTINCT FROM OLD.amount THEN
UPDATE one SET amount = json_amount;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
CREATE TRIGGER test_trigger
AFTER INSERT OR UPDATE
OF data ON one
FOR EACH ROW
EXECUTE PROCEDURE test();
I'm trying to do this without declaring any variables. for example, I've tried these:
IF one.data->'name' IS DISTINCT FROM OLD.name THEN
or
IF one.data->'name'::text IS DISTINCT FROM OLD.name THEN
or
IF ((data->'name') from one) IS DISTINCT FROM OLD.name THEN
but none work.
Making such a trigger may be a horrible idea, and that would be nice to know, but I'm mainly interested figuring out this json stuff :)
I've made an example here:
https://dbfiddle.uk/?rdbms=postgres_11&fiddle=92f2e6dd3630c76178ca4cfe4dc30b10
Upvotes: 1
Views: 696
Reputation: 2733
Although the answer provided by @sticky bit goes into the right direction, it doesn't handle type conversion well.
Here's a version that is better at dealing with empty JSON strings:
CREATE OR REPLACE FUNCTION test()
RETURNS trigger AS
$$
BEGIN
NEW.name := (NEW.data->>'name');
NEW.amount := round((NEW.data->>'amount')::numeric)::int;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
CREATE TRIGGER test_trigger
BEFORE INSERT OR UPDATE
OF data ON one
FOR EACH ROW
EXECUTE PROCEDURE test();
https://dbfiddle.uk/?rdbms=postgres_11&fiddle=15a232d4092e23c7bc0425b849b31976
Please remember that executing multiple UPDATE
s inside a trigger function after an original INSERT
or UPDATE
is going to kill performance.
Upvotes: 1
Reputation: 37472
Make it a before trigger and instead of an UPDATE
manipulate new
.
CREATE OR REPLACE FUNCTION test()
RETURNS trigger
AS
$$
BEGIN
new.name := new.data->'name';
new.amount := new.data->'amount';
RETURN new;
END;
$$
LANGUAGE plpgsql;
CREATE TRIGGER test_trigger
BEFORE INSERT
OR UPDATE
OF data
ON one
FOR EACH ROW
EXECUTE PROCEDURE test();
Upvotes: 2