应直接比对OLD.字段名与NEW.字段名:MySQL用<=>(安全等于),PostgreSQL用IS DISTINCT FROM,SQL Server的UPDATE()仅表示字段出现在SET中,不保证值变更;需规避NULL比较陷阱、避免嵌套IF及触发器内查表等性能风险。

UPDATE 触发器里怎么知道某个字段真的变了
直接比对 OLD.字段名 和 NEW.字段名,别信 IF UPDATE(字段名)(SQL Server 专属,MySQL / PostgreSQL 根本不认这个语法)。
常见错误是写成 IF UPDATE(email) 然后在 MySQL 里报错 Unknown function 'UPDATE';或者在 PostgreSQL 里误以为 pg_trigger_depth() 能判断字段变更——它只管嵌套层数,不管字段值。
- MySQL / MariaDB:必须显式写
IF OLD.email != NEW.email,注意 NULL 比较要用(安全等于)而不是!= - PostgreSQL:用
OLD.email IS DISTINCT FROM NEW.email,它天然处理 NULL,比!=可靠 - SQL Server:
UPDATE(字段名)只表示该字段出现在 SET 子句里,哪怕设成和原来一样的值也算“被更新”,不能代表真实变更
NULL 值比较踩坑最频繁
字段从 NULL 改成 'abc',或反过来,用 != 会得到 UNKNOWN,触发器逻辑直接跳过——因为 SQL 里 NULL != 'abc' 不返回 TRUE,而是 UNKNOWN,而 IF 只响应 TRUE。
- MySQL:改用
,比如OLD.phone NEW.phone返回FALSE才说明真变了 - PostgreSQL:坚持用
IS DISTINCT FROM,它把NULL和非NULL当作不同值处理 - 别写
OLD.col IS NULL AND NEW.col IS NOT NULL这种长条件,易漏、难维护
多个字段变更要合并判断,别堆 IF 嵌套
想同时监控 status 和 updated_at,不是写两层 IF,而是用布尔表达式一次兜住。
- 推荐写法:
IF (OLD.status IS DISTINCT FROM NEW.status) OR (OLD.updated_at IS DISTINCT FROM NEW.updated_at) - 避免写成
IF OLD.status != NEW.status THEN ... ELSEIF OLD.updated_at != NEW.updated_at THEN ...,逻辑割裂且漏掉两者都变的情况 - 如果后续要加字段,只需在 OR 后追加一项,结构稳定
触发器性能敏感点:别在 UPDATE 触发器里查表或调函数
每次更新一行就触发一次,如果里面执行 SELECT COUNT(*) FROM logs WHERE user_id = NEW.user_id,100 行批量更新就会查 100 次表——慢得肉眼可见。
- 只做轻量判断和字段赋值,比如
NEW.updated_at := NOW()或NEW.version := OLD.version + 1 - 需要关联查询的逻辑,挪到应用层或用异步任务处理
- PostgreSQL 中慎用
EXECUTE动态 SQL,MySQL 中避免在触发器里调用存储函数(尤其含查询的)
字段变更判断本身开销极小,但一旦混进 IO 或复杂计算,就从“透明钩子”变成“性能瓶颈”。真正难的从来不是写对逻辑,而是守住触发器的边界——它只该干一件事:响应行级变更,并且快。










