如何在触发器函数中使用 json 字符串元素而不将其声明为变量
How to use json string element in a trigger function without declaring it as a variable
我正在学习 Postgresql - 我心中没有特定的应用程序,但一般的想法是用 json 更新一列而不用担心其余的。
我有一个包含几列(id、名称、数量、json)的 table。我只想更新 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
但 none 有效。
制作这样的触发器可能是一个可怕的想法,知道这一点会很高兴,但我主要感兴趣的是弄清楚这个 json 东西 :)
我在这里做了一个例子:
https://dbfiddle.uk/?rdbms=postgres_11&fiddle=92f2e6dd3630c76178ca4cfe4dc30b10
将其设为前置触发器,而不是 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();
虽然@sticky bit 提供的答案方向正确,但它不能很好地处理类型转换。
这是一个更擅长处理空 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
请记住,在原始 INSERT
或 UPDATE
之后在触发器函数中执行多个 UPDATE
会降低性能。
我正在学习 Postgresql - 我心中没有特定的应用程序,但一般的想法是用 json 更新一列而不用担心其余的。
我有一个包含几列(id、名称、数量、json)的 table。我只想更新 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
但 none 有效。
制作这样的触发器可能是一个可怕的想法,知道这一点会很高兴,但我主要感兴趣的是弄清楚这个 json 东西 :)
我在这里做了一个例子:
https://dbfiddle.uk/?rdbms=postgres_11&fiddle=92f2e6dd3630c76178ca4cfe4dc30b10
将其设为前置触发器,而不是 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();
虽然@sticky bit 提供的答案方向正确,但它不能很好地处理类型转换。
这是一个更擅长处理空 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
请记住,在原始 INSERT
或 UPDATE
之后在触发器函数中执行多个 UPDATE
会降低性能。