Postgres 创建触发器函数以在允许插入之前将值从一列复制到另一列

Posted

技术标签:

【中文标题】Postgres 创建触发器函数以在允许插入之前将值从一列复制到另一列【英文标题】:Postgres create trigger function to copy value from one column to another before allowing insert 【发布时间】:2021-10-26 20:40:59 【问题描述】:

我正在尝试在 Postgres 13 中创建一个触发器函数,在插入或更新操作完成之前,它将值从两列复制到另外两列。

在更新/插入操作之前,我的表 test.run_conf 如下所示:

hostname run_config_current run_config_current_hash run_config_last run_config_last_hash
switch01 old_txt_str 32314322

当发生更新/插入时,我希望将 run_config_current 中的值复制到 run_config_last 并将 run_config_current_hash 复制到 run_config_last_hash。然后允许对列 run_config_current 进行更新/插入操作 - 之后触发器函数将重新计算 run_config_current_hash 的值。

为了测试,我运行以下查询将新数据插入到列run_config_current

INSERT INTO test.run_conf (hostname, run_config_current) VALUES ('switch01', 'new_txt_str' )
ON CONFLICT (hostname) DO UPDATE SET run_config_current = excluded.run_config_current

但是发生的情况是表 test.run_conf 更新如下 - 新值被插入到 run_config_currentrun_config_last 两列中:

hostname run_config_current run_config_current_hash run_config_last run_config_last_hash
switch01 new_txt_str 47173234 new_txt_str 32314322

我现在没有正常工作的触发器和功能是:

CREATE OR REPLACE FUNCTION test.run_conf_hash_gen_func()
    RETURNS TRIGGER
    SET SCHEMA 'test'
    LANGUAGE plpgsql
    AS $$
    BEGIN
        NEW.run_config_last := NEW.run_config_current;
        NEW.run_config_last_hash := NEW.run_config_current_hash;
        NEW.run_config_current_hash := MD5(NEW.run_config_current);
    RETURN NEW;
    END;
    $$
CREATE TRIGGER run_conf_hash_gen_trig
    BEFORE INSERT OR UPDATE on test.run_conf
    FOR EACH ROW
    EXECUTE PROCEDURE test.run_conf_hash_gen_func();

请帮助我理解为什么我的代码似乎将run_config_current 的新值复制到run_config_last。谢谢!

【问题讨论】:

因为在执行UPDATE 时需要OLD.run_config_current。这意味着在触发函数中,您需要区分 INSERTUPDATE,因为 INSERT 没有 OLD 值。请参阅TG_OP 此处plpgsql trigger function。 原来在 Postgres 11+ OLD for INSERT 设置为 NULL 所以你不必防范 OLD 的 pre-11 行为在 @987654351 中未分配@. 【参考方案1】:

我想通了。我的问题是我引用了 NEW var 作为要分配的值的参考。由于变量NEWOLD 是在触发器执行时设置的,但在更改之前,我的旧代码使用我通过INSERT/UPDATE 语句发送的值作为分配给我的_last 列的值。

这是我更新后的触发函数,它按预期工作。我注释掉了旧的/坏的行,下面的新行是有效的:

CREATE OR REPLACE FUNCTION test.run_conf_hash_gen_func()
    RETURNS TRIGGER
    SET SCHEMA 'test'
    LANGUAGE plpgsql
    AS $$
    BEGIN
        -- NEW.run_config_last := NEW.run_config_current;
        NEW.run_config_last := OLD.run_config_current;
        -- NEW.run_config_last_hash := NEW.run_config_current_hash;
        NEW.run_config_last_hash := OLD.run_config_current_hash;
        NEW.run_config_current_hash := MD5(NEW.run_config_current);
    RETURN NEW;
    END;
    $$

【讨论】:

当操作是 INSERT 时,这将不起作用,因为插入中没有 OLD 行。 @AdrianKlaver 是的,这实际上是所需的行为。如果它是 INSERT,则它是相关设备的全新条目,因此无需进行“最后”比较。在我的代码下一次运行时,它将从设备重新获取配置,然后在已经存在条目的地方执行更新,导致其他 2 列填充。 当我说它不起作用时,我的意思是,INSERT 将完全失败。很确定这不是你想要的。 @AdrianKlaver 嗯。我的 OP 包括我的代码将与“ON CONFICT”子句一起使用的插入查询。运行它几次它按预期工作。我还使用唯一的主机名运行简单的测试 INSERT,它们也可以正常工作。 嗯,事实证明我需要跟上文档。在 Postgres 10-'此变量在语句级触发器和 INSERT 操作中未分配。并且在 11+ '此变量在语句级触发器和 INSERT 操作中为空。'。习惯让我为 11 岁之前的行为编码。可能仍然会,但你的代码很好。

以上是关于Postgres 创建触发器函数以在允许插入之前将值从一列复制到另一列的主要内容,如果未能解决你的问题,请参考以下文章

AWS Glue - 在插入之前截断目标 postgres 表

SQL 创建触发器以在特定条件下更新表

创建触发器以在更新表之后插入

MySQL #1422 存储函数或触发器中不允许显式或隐式提交

在 Postgres 触发函数中调用异常之前执行操作

使用触发器将 postgres SQL 插入语句拆分为 2 个表