【问题标题】:PostgreSQL - Identify if an update is from a trigger or a userPostgreSQL - 确定更新是来自触发器还是用户
【发布时间】:2021-04-23 13:30:12
【问题描述】:

PostgreSQL 13.2 我有一个包含 5 个字段的表:id、val1、val2、val3、update_origin。

第 1 步:当创建或更新“val1”或“val2”时,触发器会生成“val3”。此触发器还使用“触发器更新”值更新字段“update_origin”。

第 2 步:用户也可以直接创建或更新 val3,如果是这样,我创建一个触发器,用“用户更新”值更新字段“update_origin”。

使用此设置,自动触发更新和手动用户更新之间没有区别,因为无法识别更新的来源(第二步始终正确,因为PostgreSQL 更新就是更新)。

如何区分来自触发器(第 1 步示例)或来自用户(第 2 步示例)的更新?

序列

CREATE SEQUENCE demo_id_seq
    INCREMENT 1
    START 1
    MINVALUE 1
    MAXVALUE 2147483647
    CACHE 1;

表格

CREATE TABLE demo
(
    id integer NOT NULL DEFAULT nextval('demo_id_seq'::regclass),    
    val1 character varying COLLATE pg_catalog."default",
    val2 character varying COLLATE pg_catalog."default",
    val3 character varying COLLATE pg_catalog."default",
    update_origin character varying COLLATE pg_catalog."default"
)
TABLESPACE pg_default;

功能#1

CREATE FUNCTION val3_concat()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
    UPDATE demo
    SET val3 = CONCAT(val1, ' ', val2),
    update_origin = 'trigger update'
    WHERE id = currval('demo_id_seq');
RETURN null;  
END
$BODY$;

触发器 #1

CREATE TRIGGER trigger_val3_concat
    AFTER INSERT OR UPDATE OF val1, val2
    ON demo
    FOR EACH ROW
    EXECUTE PROCEDURE val3_concat();

功能#2

CREATE FUNCTION update_origin()
    RETURNS trigger
    LANGUAGE 'plpgsql'
    COST 100
    VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
    IF OLD.val3 <> NEW.val3 THEN
        UPDATE demo
        SET update_origin = 'user update'
        WHERE id = currval('demo_id_seq');
    END IF;
RETURN NEW; 
END
$BODY$;

触发器 #2

CREATE TRIGGER trigger_update_origin
    AFTER UPDATE OF val3
    ON demo
    FOR EACH ROW
    EXECUTE PROCEDURE update_origin();

INSERT INTO demo (val1, val2) VALUES ('first', 'shot'); 表的示例将返回 1, first, shot, first shot, trigger update

如果我更新 'val1' 或 val2' 表将返回 1, first, shot, first shot, user update 或者我希望只有当我更新 'val3' 并且只有 'val3' 时才会这样。

【问题讨论】:

  • 触发器中不需要使用UPDATE。使用BEFORE 触发器并分配您想要的值:new.update_origin := 'trigger update';
  • 如果我们知道为什么您需要能够区分差异,也许会有答案。
  • 制作函数BEFORE UPDATE并测试val3的值。如果OLD.val3 IS DISTINCT FROM NEW.val3 则用户在使用该功能之前对其进行了更改。基于此,将update_origin 设置为user_update。否则让函数生成val3并将update_origin设置为trigger_update
  • @LaurenzAlbe 我的上下文:我需要知道在创建或更新“val1”或“val2”时触发器是否生成“val3”,或者用户是否手动创建或更新“val3” .这是为了可追溯性。

标签: postgresql triggers


【解决方案1】:

例子:

CREATE TABLE demo
(
    id serial NOT NULL PRIMARY KEY,    
    val1 varchar,
    val2 varchar,
    val3 varchar,
    update_origin varchar
)
;

CREATE OR REPLACE FUNCTION public.update_origin()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
BEGIN
    IF TG_OP = 'UPDATE' THEN
        IF OLD.val3 IS DISTINCT FROM NEW.val3 THEN
            NEW.update_origin = 'user update';
        ELSE
            NEW.val3 = NEW.val1 || ' ' || NEW.val2;
            NEW.update_origin = 'trigger update';
        END IF;
    ELSEIF TG_OP = 'INSERT' THEN
        IF NEW.val3 IS NOT NULL THEN
            NEW.update_origin = 'user update';
        ELSE
            NEW.val3 = NEW.val1 || ' ' || NEW.val2;
            NEW.update_origin = 'trigger update';
        END IF;
    END IF;
RETURN NEW;
END
$function$
;

CREATE TRIGGER trigger_update_origin
    BEFORE INSERT OR UPDATE
    ON demo
    FOR EACH ROW
    EXECUTE PROCEDURE update_origin();

insert into demo (val1, val2) values ('1', '2'); 
INSERT 0 1
test_(aklaver)(5432)=> select * from demo;
 id | val1 | val2 | val3 | update_origin  
----+------+------+------+----------------
  2 | 1    | 2    | 1 2  | trigger update
insert into demo (val1, val2, val3) values ('3', '4', '5');
INSERT 0 1
test_(aklaver)(5432)=> select * from demo;
 id | val1 | val2 | val3 | update_origin  
----+------+------+------+----------------
  2 | 1    | 2    | 1 2  | trigger update
  3 | 3    | 4    | 5    | user update
update demo set val1 = '6' where id = 2;
UPDATE 1
test_(aklaver)(5432)=> insert into demo (val1, val2, val3) values ('3', '4', '5');
INSERT 0 1
test_(aklaver)(5432)=> select * from demo;
 id | val1 | val2 | val3 | update_origin  
----+------+------+------+----------------
  3 | 3    | 4    | 5    | user update
  2 | 6    | 2    | 6 2  | trigger update
  4 | 3    | 4    | 5    | user update

将两个函数合二为一,并使用TG_OP 来区分操作。

【讨论】:

  • 感谢这个详细的回答,行为是我所期望的(并尝试解释)。我还不习惯触发器和之前/之后,旧/新概念。据我个人所知,new.update_origin := 'trigger update';new.update_origin = 'trigger update'; 之间有区别吗?显然不是。
  • 没有区别,见assignment
猜你喜欢
  • 2010-10-18
  • 1970-01-01
  • 1970-01-01
  • 2021-09-08
  • 1970-01-01
  • 1970-01-01
  • 2016-03-23
  • 1970-01-01
  • 2012-06-13
相关资源
最近更新 更多