You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL中row(NEW.*) IS DISTINCT FROM row(OLD.*)工作原理及代码疑问

PostgreSQL触发器中row(NEW.) IS DISTINCT FROM row(OLD.)的工作原理及代码疑问解答

1. row(NEW.) IS DISTINCT FROM row(OLD.)的工作机制

  • row(NEW.*) 和 row(OLD.*) 会分别将触发器触发时的新行、旧行所有字段打包成一条匿名行记录(row type)。
  • IS DISTINCT FROM 是PostgreSQL专门处理含NULL值的不等判断运算符:
    • 它会逐字段对比两个行记录的所有值
    • 不同于普通<>运算符,它将NULL视为确定值——如果某个字段在NEW中是NULL、OLD中不是,或反过来,会被判定为不同
    • 只要两个行记录中有任意一个字段的值(包括NULL状态)不同,整个表达式返回true;只有所有字段完全一致时才返回false

2. 使用NEW.*和OLD.*是否冗余?

不冗余,这段代码的核心逻辑是检测行数据是否发生了任何变更:

  • 如果执行UPDATE语句但未修改任何字段值(比如UPDATE users SET name = name WHERE id=1),触发器返回OLD,避免数据库执行无意义的更新操作,减少日志和锁的不必要开销。
  • 只要有任何一个字段被修改,才会进入分支重置created_at并更新updated_at,完全匹配业务需求——只有实际变更时才更新时间戳。

3. 为何不直接用NEW.created_at <> OLD.created_at判断?

这个思路完全不符合需求,原因有两点:

  • created_at是业务上要保持不变的字段(触发器会强制将其重置为OLD.created_at),用户的更新操作大概率不会修改这个字段,用它的对比根本检测不到其他字段的变更(比如修改用户名、邮箱等)。
  • 就算用户误改了created_at,触发器也会把它改回原值,但此时用created_at的对比触发逻辑,并不是我们要检测的核心——我们需要的是任何字段的变更,而非仅盯着created_at。

原触发器代码

-- always reset the timestamp to the old value ("actual creation time")
CREATE OR REPLACE FUNCTION before_update_users()
  RETURNS trigger AS
$BODY$
BEGIN
  -- always reset the timestamp to the old value ("actual creation time")
  IF row(NEW.*) IS DISTINCT FROM row(OLD.*) THEN
      NEW.created_at := OLD.created_at;
      NEW.updated_at = now();
      RETURN NEW;
   ELSE
      RETURN OLD;
   END IF;
END;
$BODY$

内容的提问来源于stack exchange,提问作者Nguyen Bang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:40:25