sql server中通过count(*)统计inserted和deleted表行数可准确判断操作类型:insert时inserted>0且deleted=0,update时两者均>0,delete时inserted=0且deleted>0;if update(col)仅检测列是否出现在set子句,不反映实际值变化。

直接看 inserted 和 deleted 这两张虚拟表是否存在数据,就能准确判断操作类型——不需要查系统视图、也不依赖触发器名称或注释。
用 COUNT(*) 判断 INSERT/UPDATE/DELETE 最稳
很多人用 EXISTS(SELECT 1 FROM inserted),但实际在批量操作(比如一次插入 1000 行)时,EXISTS 没问题,可读性不如直接计数清晰。更关键的是:某些 SQL Server 版本在空表触发时,EXISTS 可能因执行计划缓存导致误判,而 COUNT(*) 更可靠。
@inserted_count INT = (SELECT COUNT(*) FROM inserted)@deleted_count INT = (SELECT COUNT(*) FROM deleted)IF @inserted_count > 0 AND @deleted_count = 0 → INSERTIF @inserted_count > 0 AND @deleted_count > 0 → UPDATEIF @inserted_count = 0 AND @deleted_count > 0 → DELETE
AFTER 触发器里不能用 IF UPDATE(col) 判断字段是否被改
IF UPDATE(column_name) 是个常见误区:它只表示该列出现在 UPDATE 语句的 SET 子句中,并不表示值真的变了。比如 UPDATE t SET name = name,UPDATE(name) 返回 true,但数据没变——这会导致日志冗余或业务逻辑错判。
- 真要判断字段值是否变化,必须显式比对:
FROM inserted i JOIN deleted d ON i.id = d.id WHERE i.name d.name OR (i.name IS NULL AND d.name IS NOT NULL) OR (i.name IS NOT NULL AND d.name IS NULL) -
IF UPDATE()仅适合“只要列被指定就响应”的场景,比如审计字段更新时间戳
INSTEAD OF 触发器中 inserted/deleted 行为一致,但语义不同
INSTEAD OF 触发器里 inserted 和 deleted 的存在规则和 AFTER 完全一样,但含义更需小心:它发生在原操作执行前,所以你手动 INSERT/UPDATE/DELETE 时,不能再依赖原语句的副作用(比如自增 ID 已生成但未提交)。
-
INSTEAD OF INSERT中,inserted有数据,deleted为空 —— 但你如果在里面做INSERT INTO ... SELECT *,要注意主键冲突或约束检查时机 - 不要在
INSTEAD OF里再调用可能再次触发同类型触发器的操作(比如在INSTEAD OF UPDATE里又UPDATE同一表),否则容易嵌套爆栈
真正容易被忽略的是:SQL Server 不保证触发器内 inserted 和 deleted 的行顺序与原始 DML 语句一致,尤其在批量操作时。如果业务逻辑依赖顺序(比如按时间戳取第一条),必须加 ORDER BY 显式排序,不能靠自然顺序。











