如何在触发函数中使用json字符串元素而不将其声明为变量

时间:2019-05-05 16:26:53

标签: postgresql postgresql-11

我正在学习Postgresql-我没有特定的应用程序,但是总体思路是用json更新一列,而不必担心其余的事情。

我有一个带有几列的表格(id,名称,金额,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 个答案:

答案 0 :(得分:2)

将其设为前触发器,而不是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 :(得分:1)

尽管@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

请记住,在原始UPDATEINSERT之后在触发器函数中执行多个UPDATE会降低性能。