查询优化器对您的 T-SQL 批处理进行静态解析,一旦看到 MERGE 语句,它就会验证需求。它不会考虑影响 MERGE 语句之前的触发器的任何 DDL 语句。
您可以使用 GO 将语句分成单独的批次来解决此问题,但如果它在单个 SP(没有 GO 语句)中,您有两个选择
- 将 MERGE 放入主调用的支持 SP;或
- 使用动态 SQL
动态 SQL
让我们用触发器创建一个表
create table tg1(i int)
;
create trigger tg1_tg on tg1 instead of insert as
select 1
GO
然后尝试在表上合并
alter table tg1 disable trigger tg1_tg
;
merge tg1 as target
using (select 1 union all select 3) as source (X) on target.i = source.x
when matched then
delete
when not matched by target then
insert (i) values (x)
output $action, inserted.*, deleted.*
;
alter table tg1 enable trigger tg1_tg
;
不好……
消息 5316,第 16 级,状态 1,第 1 行
MERGE 语句的目标“tg1”在 MERGE 语句中指定的部分(但不是全部)操作上具有 INSTEAD OF 触发器。在 MERGE 语句中,如果任何操作在目标上启用了 INSTEAD OF 触发器,则所有操作都必须启用 INSTEAD OF 触发器。
所以我们使用动态SQL
alter table tg1 disable trigger tg1_tg
;
exec ('
merge tg1 as target
using (select 1 union all select 3) as source (X) on target.i = source.x
when matched then
delete
when not matched by target then
insert (i) values (x)
output $action, inserted.*, deleted.*
;')
alter table tg1 enable trigger tg1_tg
;
支持程序
让我们创建一个执行 MERGE 的过程(生产过程可能会有一个表变量,使用 #temp 表或接受一些参数)
create proc tg1_MERGE as
merge tg1 as target
using (select 1 union all select 3) as source (X) on target.i = source.x
when matched then
delete
when not matched by target then
insert (i) values (x)
output $action, inserted.*, deleted.*
;
GO
不行……
消息 5316,第 16 级,状态 1,第 1 行
MERGE 语句的目标“tg1”在 MERGE 语句中指定的部分(但不是全部)操作上具有 INSTEAD OF 触发器。在 MERGE 语句中,如果任何操作在目标上启用了 INSTEAD OF 触发器,则所有操作都必须启用 INSTEAD OF 触发器。
即使要创建它,您也需要禁用触发器 - 所以禁用触发器并再次创建 proc - 这次它会起作用。
最后,你可以运行这个有效的批处理
alter table tg1 disable trigger tg1_tg
;
exec tg1_MERGE
;
alter table tg1 enable trigger tg1_tg
;