after触发器不感知merge语句本身,仅响应其分解出的insert/update/delete物理操作;同一merge可能混合触发多类触发器,需依$action输出结果严格对齐逻辑。

AFTER 触发器不是“无法捕获 MERGE”,而是它根本不感知 MERGE 这个语句本身——它只响应底层实际执行的 INSERT、UPDATE 或 DELETE 动作。
你看到的“没触发”,往往是因为 MERGE 实际没做对应操作(比如全走 WHEN NOT MATCHED 插入,那只有 INSERT 触发器被调),或者你查错了触发器类型(比如只建了 AFTER UPDATE,但 MERGE 实际执行的是 DELETE)。
MERGE 到底触发哪些触发器?
-
MERGE是逻辑语句,SQL Server 会将其拆解为物理 DML:-
WHEN MATCHED THEN UPDATE→ 触发AFTER UPDATE -
WHEN MATCHED THEN DELETE→ 触发AFTER DELETE -
WHEN NOT MATCHED THEN INSERT→ 触发AFTER INSERT
-
- 同一
MERGE语句可能混合触发多个类型(例如部分行更新、部分行插入),每个类型各自独立触发对应触发器 -
AFTER触发器不会因为 MERGE 语句执行就“统一触发一次”,而是按实际修改的每批行,在对应 DML 类型下触发
常见错误现象:
- 只建了
AFTER INSERT,但测试数据全命中WHEN MATCHED→ 看起来“没触发” - 在
AFTER UPDATE里查DELETED表,但某次 MERGE 实际只插入没更新 →DELETED为空,逻辑报错或漏处理 - 忘记
WHEN NOT MATCHED BY SOURCE也会触发DELETE,但很多人只关注前两个子句
如何确认 MERGE 真正执行了什么?
别靠猜,用 OUTPUT 子句直接看:
MERGE target AS t USING source AS s ON t.id = s.id WHEN MATCHED THEN UPDATE SET t.val = s.val WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) OUTPUT $action, INSERTED.*, DELETED.*;
返回结果第一列就是 $action('INSERT' / 'UPDATE' / 'DELETE'),这才是 MERGE 实际干的事。你建的触发器必须和这个动作严格对齐。
注意:$action 是 MERGE 特有,触发器内部不可用;触发器只能靠 INSERTED 和 DELETED 表是否存在数据来间接判断。
AFTER UPDATE 触发器里怎么安全读旧值和新值?
MERGE 执行 UPDATE 时,DELETED 和 INSERTED 的行为与普通 UPDATE 完全一致,但有两个关键陷阱:
-
DELETED中的值是上一次提交后的快照,不是原始业务值(如果之前有其他触发器改过该行,这里就是改过的值) - 如果 MERGE 更新了主键字段(如
SET id = s.new_id),INSERTED.id和DELETED.id不再相等 → 用ON i.id = d.id关联会失败
正确做法:
- 优先用自然键(如业务单号、时间戳+类型组合)关联
INSERTED和DELETED,而不是依赖可能变更的主键 - 显式检查交集是否非空,避免空 JOIN 导致无记录写入审计表:
IF EXISTS (SELECT 1 FROM INSERTED i INNER JOIN DELETED d ON i.order_no = d.order_no) BEGIN INSERT INTO audit_log (...) SELECT i.order_no, d.status, i.status, GETDATE() FROM INSERTED i INNER JOIN DELETED d ON i.order_no = d.order_no; END
兼容性级别影响触发器行为吗?
不影响触发器是否触发,但会影响 MERGE 解析和优化路径。SQL Server 2017 默认兼容级别是 140,而 MERGE 自 SQL Server 2008(100)起就已支持。不过:
- 低兼容级别(如
90或80)下,MERGE可能被重写为更保守的执行计划,影响性能但不改变触发逻辑 - 升级到
140或更高后,OUTPUT子句支持更稳定,$action返回更可靠 - 若数据库兼容级别低于
100(极罕见),MERGE语句本身会报错,根本走不到触发器环节
所以,如果你发现 MERGE “不触发”,先查 @@ERROR 和执行计划,而不是怀疑兼容级别。
真正容易被忽略的点是:MERGE 的每个子句是独立评估的,一行不可能同时走 INSERT 和 UPDATE;触发器响应的是最终落地的物理操作,不是 MERGE 的语法分支。










