最稳办法是用COUNT(*)查inserted和deleted行数:INSERT时inserted>0且deleted=0,UPDATE时两者均>0;IF UPDATE(col)仅表示列出现在SET子句,不反映实际值变化,真判断需显式比对新旧值。

直接查 inserted 和 deleted 这两张虚拟表的行数,是最稳、最通用的办法。 不依赖触发器类型(AFTER 或 INSTEAD OF),也不受批量操作或空表场景干扰。
SQL Server 里用 COUNT(*) 判断 INSERT/UPDATE 最可靠
很多人用 EXISTS(SELECT 1 FROM inserted),但实际在某些 SQL Server 版本 + 执行计划缓存组合下,可能误判空表触发场景。而 COUNT(*) 是确定性计算,不会出错。
@inserted_count INT = (SELECT COUNT(*) FROM inserted)@deleted_count INT = (SELECT COUNT(*) FROM deleted)- 若
@inserted_count > 0 AND @deleted_count = 0→ 是INSERT - 若
@inserted_count > 0 AND @deleted_count > 0→ 是UPDATE
别把 IF UPDATE(col) 当成“字段值变了”的判断依据
IF UPDATE([status]) 只表示 status 出现在 UPDATE ... SET 子句里,哪怕写的是 SET status = status,它也返回 true。这跟业务上“字段实际被修改”不是一回事。
- 真要检测值是否变化,必须显式比对:
i.status != d.status OR (i.status IS NULL AND d.status IS NOT NULL) OR (i.status IS NOT NULL AND d.status IS NULL) -
IF UPDATE()适合做“只要列被指定就响应”的轻量逻辑,比如统一更新updated_at字段
PostgreSQL 和 MySQL 的写法更直白,但语义不同
PostgreSQL 触发器函数里直接读 TG_OP 变量:IF TG_OP = 'INSERT' 或 IF TG_OP = 'UPDATE';MySQL 则靠 NEW 和 OLD 是否为 NULL 来区分——NEW 有值、OLD 为空是 INSERT,两者都有值是 UPDATE。
- 注意 PostgreSQL 的
BEFORE触发器中,RETURN NEW可修改数据;AFTER中RETURN NULL或RETURN NEW都无实质影响 - MySQL 的
IF UPDATE('col')同样只看字段是否出现在SET子句,不反映真实变更
最容易被忽略的是:SQL Server 不保证 inserted 和 deleted 表中行的顺序与原始 DML 一致,尤其在批量操作时。如果业务逻辑依赖顺序(比如取“最新一条变更”),必须加 ORDER BY 显式排序,不能靠默认顺序。

















