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需要先校验两个记录的字段数量、类型、顺序是否完全匹配:- 如果表中存在隐藏字段、生成列,
row(NEW.*)不会包含这些字段,导致比较的行内容不完整; - 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
相关产品推荐
相关产品推荐

