编写SQL触发器:筛选不存在于新数据的删除行插入目标表
问题根因
你的代码存在两个核心问题,导致触发逻辑不符合预期:
- 语法不完整:
WHERE NOT EXISTS的子句缺少右括号,直接执行会抛出语法错误 - 触发逻辑和业务场景不匹配:你是分两步执行
DELETE→INSERT操作,两个操作会分别触发触发器:- 执行
DELETE时,只有deleted表有数据,inserted表为空,NOT EXISTS条件永远成立,所有被删除的数据都会被插入历史表 - 执行
INSERT时,只有inserted表有数据,deleted表为空,不会执行任何插入操作
- 执行
解决方案
方案1(优先推荐):改用MERGE原子操作实现删插逻辑
将你外部的先删除再插入的操作替换为单个MERGE原子操作,触发触发器时deleted和inserted会同时存在数据,你的匹配逻辑即可正常生效。
调整后的触发器代码:
CREATE TRIGGER newTrigger ON SomeTable AFTER DELETE, INSERT AS SET NOCOUNT ON; -- 建议显式指定目标表插入字段,避免后续表结构变更出错 INSERT INTO SAP_ZINDICADORES_CDU_HIS_D (NumeroPedido, Material, Paquete, 创建时间) SELECT old.NumeroPedido ,old.Material ,old.Paquete ,GETDATE() FROM deleted old WHERE NOT EXISTS ( SELECT 1 FROM inserted new -- 如果判断行是否存在的依据不是IdRow,可替换为实际业务唯一键组合 WHERE new.IdRow = old.IdRow );
业务侧删插逻辑替换为MERGE语句:
MERGE INTO SomeTable AS target USING 你的新数据源表 AS source ON target.IdRow = source.IdRow -- 按唯一键匹配新旧数据 -- 已有匹配行则更新字段 WHEN MATCHED THEN UPDATE SET target.NumeroPedido = source.NumeroPedido, target.Material = source.Material, target.Paquete = source.Paquete -- 新数据有、旧表没有的行新增 WHEN NOT MATCHED BY TARGET THEN INSERT (IdRow, NumeroPedido, Material, Paquete) VALUES (source.IdRow, source.NumeroPedido, source.Material, source.Paquete) -- 新数据没有、旧表有的行删除 WHEN NOT MATCHED BY SOURCE THEN DELETE;
该方案原子性更高,不会出现删完插失败的数据不一致问题,完全适配你的需求。
方案2:无法修改外部删插逻辑时,用缓存表过渡
如果业务侧不能调整先删后插的逻辑,可通过中间缓存表暂存删除的数据,等插入操作触发时再做比对:
1. 先创建缓存表
CREATE TABLE SomeTable_DeleteCache ( IdRow INT PRIMARY KEY, -- 和原表唯一键类型保持一致 NumeroPedido [对应字段类型], Material [对应字段类型], Paquete [对应字段类型], CacheTime DATETIME DEFAULT GETDATE() );
2. 调整触发器逻辑
CREATE TRIGGER newTrigger ON SomeTable AFTER DELETE, INSERT AS SET NOCOUNT ON; -- DELETE操作触发:缓存删除的数据 IF EXISTS(SELECT 1 FROM deleted) AND NOT EXISTS(SELECT 1 FROM inserted) BEGIN INSERT INTO SomeTable_DeleteCache (IdRow, NumeroPedido, Material, Paquete) SELECT IdRow, NumeroPedido, Material, Paquete FROM deleted; END -- INSERT操作触发:比对缓存和新插入数据,写入历史表后清空缓存 IF EXISTS(SELECT 1 FROM inserted) AND NOT EXISTS(SELECT 1 FROM deleted) BEGIN INSERT INTO SAP_ZINDICADORES_CDU_HIS_D (NumeroPedido, Material, Paquete, 创建时间) SELECT c.NumeroPedido, c.Material, c.Paquete, GETDATE() FROM SomeTable_DeleteCache c WHERE NOT EXISTS ( SELECT 1 FROM inserted i WHERE i.IdRow = c.IdRow ); TRUNCATE TABLE SomeTable_DeleteCache; END
注意该方案需要确保每次DELETE之后一定会执行INSERT,否则缓存表会残留无效的旧数据。
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

