当 NEW.field 为 NULL 时 PostgreSQL 不触发

Posted

技术标签:

【中文标题】当 NEW.field 为 NULL 时 PostgreSQL 不触发【英文标题】:PostgreSQL not triggering when NEW.field is NULL 【发布时间】:2016-09-21 09:13:16 【问题描述】:

我的客户表上有以下触发器,因此我可以跟踪客户名称是否随时间变化以及名称的先前版本是什么。

CREATE OR REPLACE FUNCTION fn_customer_changes_log_history() RETURNS trigger as 
$BODY$
    BEGIN
       IF (NEW.name <> OLD.name)  
    THEN
        INSERT INTO tbl_customers_history(customer_id, name, action)
        VALUES(OLD.id, OLD.name, 'UPDATE');
    END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;

CREATE TRIGGER tr_customer_changes_log_history
BEFORE UPDATE ON tbl_customers
FOR EACH ROW
EXECUTE PROCEDURE fn_customer_changes_log_history();

但是当我在 NEW.name = NULL 并且 OLD.name = "Customer 1" 时执行更新时,不会触发此触发器?它仅在 NEW.name 此处有实际字符串值时触发。

这是为什么呢? NULL 和“客户 1”不相等所以应该触发?

【问题讨论】:

【参考方案1】:

使用

IF (NEW.name IS DISTINCT FROM OLD.name)

而不是

IF (NEW.name <> OLD.name)

【讨论】:

将其与 COALESCE 已建议的答案进行比较时会如何? 可能会少一些操作,但我预计不会有可测量的差异。不过我更喜欢我的版本,因为它更容易阅读。【参考方案2】:

尝试将条件更改为

IF COALESCE((NEW.name <> OLD.name), true)

问题在于,如果NEW.fieldNULL,那么无论OLD.name 的值是多少,NEW.name &lt;&gt; OLD.name 的计算结果都是NULL。在有条件的情况下,NULL 始终被视为 false。

一个更正确的答案应该是

IF COALESCE((NEW.name <> OLD.name), true) AND NOT (NEW.name IS NULL AND OLD.name IS NULL)

处理NEW.nameOLD.name 都是NULL 的情况,以便在这种情况下它们不会被视为平等。

【讨论】:

谢谢。这行得通。但是你知道为什么它不会像我那样触发吗?为什么需要这种 COALESCE 的东西? @koala 我扩展了我的答案。【参考方案3】:

NULL &lt;&gt; 'anything' 的计算结果始终为 FALSE。就像NULL = 'anything'NULL &gt; 5

您需要coalesce 来修复比较:

COALESCE(NEW.name, '') <> COALESCE(OLD.name, '')

【讨论】:

以上是关于当 NEW.field 为 NULL 时 PostgreSQL 不触发的主要内容,如果未能解决你的问题,请参考以下文章

Web API POST 请求添加新记录,但是所有属性都设置为 NULL?

更新查询 - 设置 new_field = existing_field

当我通过 POST 发送大于 20MB 的视频文件时,为啥 json 响应为 NULL?

使用json.net实现复杂对象转换为QueryString

Asp.net core MVC post 参数始终为空

触发器 - 将 SELECT Result 直接写入 :new.field