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

PostgreSQL触发器中row值与NEW使用IS DISTINCT FROM的差异问询

PostgreSQL触发器中row(NEW.*)与NEW的比较行为差异解释

核心差异:表行类型 vs 匿名记录的比较逻辑

PostgreSQL里,触发器中的NEW和OLD是绑定对应表结构的表行类型变量,而row(NEW.*)是手动构造的匿名复合记录,二者用IS DISTINCT FROM比较时,行为有本质区别:

  • 表行类型比较(NEW IS DISTINCT FROM OLD)
    这种比较严格遵循表的定义,逐字段校验所有用户定义字段、隐含系统字段(如存在)以及生成列。只要任意一个字段出现「非NULL vs NULL」或者值不相等的情况,就会返回true,完全符合行变更的预期判断。

  • 匿名记录比较(row(NEW.*) IS DISTINCT FROM row(OLD.*))
    row(NEW.*)会把NEW的字段拆解后重新打包成一个匿名复合类型,它的类型并非原表的行类型。当和另一个匿名记录比较时,PostgreSQL需要先校验两个记录的字段数量、类型、顺序是否完全匹配:

    1. 如果表中存在隐藏字段、生成列,row(NEW.*)不会包含这些字段,导致比较的行内容不完整;
    2. Liquibase执行更新时,可能会对字段值做隐式类型转换(比如字符串和NULL的传递方式),导致匿名记录的字段类型与原表行类型出现细微差异,PostgreSQL无法正确识别字段的非NULL vs NULL差异,最终返回错误的false结果。

为什么手动执行和Liquibase执行表现不同?

手动执行UPDATE时,通常只修改目标字段,NEW变量中仅被修改的字段值变化,其他字段保持与OLD一致,且不存在Liquibase执行时的隐式字段处理逻辑,此时row(NEW.*)的构造和比较恰好能命中正确结果;而Liquibase会生成包含所有字段的UPDATE语句(即使未修改的字段也会被赋值为原值),加上可能的隐式类型转换,触发了匿名记录比较的逻辑漏洞。

结论

在PostgreSQL触发器中判断行是否变更,始终优先使用NEW IS DISTINCT FROM OLD,它直接利用表行类型的原生比较逻辑,避免匿名记录构造带来的类型、字段遗漏等问题。

内容的提问来源于stack exchange,提问作者Dmitry Gusev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 22:27:23