Как использовать строковый элемент json в функции триггера, не объявляя его как переменную - PullRequest
1 голос
/ 05 мая 2019

Я изучаю Postgresql - я не имею в виду конкретное приложение, но общая идея - обновить один столбец с помощью json и не беспокоиться об остальных.

У меня есть таблица с несколькими столбцами (идентификатор, имя, сумма, JSON). Я хотел бы обновить только столбец json и иметь триггер для обновления других столбцов из json.

Например, если для json задано значение {"name" = "fred", "amount" = 100}, триггер заполняет столбцы имени и суммы

Пока это работает:

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();

Я пытаюсь сделать это без объявления каких-либо переменных. например, я пробовал это:

IF one.data->'name' IS DISTINCT FROM OLD.name THEN

или

IF one.data->'name'::text IS DISTINCT FROM OLD.name THEN

или

IF ((data->'name') from one) IS DISTINCT FROM OLD.name THEN

но никто не работает.

Создание такого триггера может быть ужасной идеей, и это было бы неплохо знать, но я в основном заинтересован в выяснении этого материала json:)

Я привел пример здесь:

https://dbfiddle.uk/?rdbms=postgres_11&fiddle=92f2e6dd3630c76178ca4cfe4dc30b10

Ответы [ 2 ]

2 голосов
/ 05 мая 2019

Сделайте это перед триггером и вместо UPDATE манипулируйте 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();
1 голос
/ 05 мая 2019

Хотя ответ, предоставляемый битом @sticky, идет в правильном направлении, он плохо обрабатывает преобразование типов.

Вот версия, которая лучше справляется с пустыми строками JSON:

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

Помните, что выполнение нескольких UPDATE s внутри функции триггера после того, как исходный INSERT или UPDATE приведет к снижению производительности.

...