SQL Server 的 UPDATE() 函数仅检测列是否出现在 SET 子句中,不判断值是否变化;必须用于 IF 或 WHERE 中,参数为列名,对 NULL 赋值也返回 1,对计算列等始终返回 0。

SQL Server 的 UPDATE() 函数怎么用才靠谱
UPDATE() 是 SQL Server 触发器里判断“某列是否在本次 UPDATE 语句中被显式修改”的唯一内置方式。它不看值变没变,只看列有没有出现在 SET 子句里——这点非常关键,很多人误以为它检测“值是否变化”,结果逻辑出错。
实操建议:
-
UPDATE()必须写在IF或WHERE中,不能单独执行;参数必须是列名(如UPDATE(Email)),不能是变量或表达式 - 对
NULL值赋值也算“被更新”,比如SET Email = NULL会让UPDATE(Email)返回1 - 如果
UPDATE语句里根本没提某列(哪怕它实际值变了),UPDATE(ColumnName)就返回0 - 注意:它对计算列、标识列、
ROWVERSION列始终返回0,哪怕它们被间接影响
MySQL 没有 UPDATE(),得靠 OLD 和 NEW 手动比
MySQL 触发器没有等价函数,必须用 OLD.column_name != NEW.column_name(或更严谨地处理 NULL)来判断字段值是否真变了。这和 SQL Server 的语义完全不同——这里比的是“值是否不同”,不是“是否被 SET”。
实操建议:
- 直接写
OLD.email != NEW.email在NULL场景下会失效(因为NULL != NULL为UNKNOWN),必须用OLD.email NEW.email = 0或显式判断IS NULL - 字符串比较要注意大小写和末尾空格,建议统一用
BINARY或指定 collation 避免隐式转换陷阱 - 如果字段类型是
JSON、POINT等复杂类型,!=可能不准确,优先用JSON_CONTAINS()+JSON_EXTRACT()拆解比对关键路径
PostgreSQL 用 tg_argv 或条件触发器更灵活,但别滥用
PostgreSQL 没有内置字段级变更检测函数,常规做法是在触发器函数里手动比较 OLD.col 和 NEW.col。不过从 9.0 起支持条件触发器(WHEN (NEW.col IS DISTINCT FROM OLD.col)),语法简洁且天然处理 NULL。
实操建议:
- 条件触发器的
WHEN子句里只能用OLD/NEW字段,不能调函数或子查询;想复用逻辑就封装成IMMUTABLE函数 - 如果要同时监控多个字段且逻辑不同,别堆一个大触发器,拆成多个独立触发器更易维护、性能也更好(避免全量扫描)
-
tg_argv可传参给触发器函数,适合同一函数复用于不同表/字段场景,但参数是文本数组,需手动解析,容易出错,非必要不推荐
跨数据库移植时最容易栽的三个坑
写触发器时一旦考虑兼容性,UPDATE() 这类方言特性就是雷区。同一套业务逻辑在不同数据库上行为可能完全相反。
常见错误现象:
- SQL Server 里
UPDATE(name)为真 → MySQL 里OLD.name != NEW.name却为假(比如只改了空格或大小写) - PostgreSQL 条件触发器用了
IS DISTINCT FROM,迁到 SQL Server 就报语法错误 - 在 MySQL 里用
CONCAT(OLD.col, '') != CONCAT(NEW.col, '')模拟非空安全比较,结果遇到TEXT字段超长截断,误判为“未变更”
真正麻烦的不是语法差异,而是语义差异本身:有的检测“是否被提及”,有的检测“是否真变”,有的甚至要区分“是否由用户发起”。选哪种,得先想清楚你到底要响应什么事件——是业务层明确改了邮箱,还是数据最终值变了,这决定了底层怎么写,而不是反过来。










