UPDATE 触发器在批量更新时运行多次
Trigger for UPDATE runs many time on batch Updates
我的所有表都有用于 CRUD 操作的触发器。
这是一个示例:
ALTER TRIGGER [dbo].[Cities_tr] ON [dbo].[Cities] AFTER INSERT, UPDATE
AS
BEGIN
DECLARE @operation CHAR(6)
SET @operation = CASE WHEN EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
THEN 'Update'
WHEN EXISTS (SELECT * FROM inserted)
THEN 'Insert'
WHEN EXISTS(SELECT * FROM deleted)
THEN 'Delete'
ELSE NULL
END
IF @operation = 'Insert'
INSERT INTO history ([dt],[tname],[cuser] ,[id],op)
SELECT GETDATE(),'Cities', i.ldu, i.CityId,@operation
FROM inserted i
set nocount on
IF @operation = 'Update'
INSERT INTO history ([dt],[tname],[cuser] ,[id],op)
SELECT GETDATE(),'Cities', i.ldu, i.CityId,@operation
FROM deleted d, inserted i
END
如果我更新一行,一切正常,触发器会在历史记录中插入一行。
例如
update top(1) cities set f=1
但是如果更新了不止一行,将插入updatedrow^2行。
例如 9 表示 3 行 100 表示 10 行...
我的触发器出了什么问题,我该如何解决?
您的代码存在问题,您正在交叉连接 inserted
和 deleted
。在多行更新中,两者都包含许多行,笛卡尔积相乘。
您似乎真的想记录“新”行(插入或更新)。如果是这样,您不想从 deleted
select。此外,条件逻辑可以在单个查询中移动,这样可以简化您的代码,如下所示:
ALTER TRIGGER dbo.Cities_tr
ON dbo.Cities
AFTER INSERT, UPDATE
AS
BEGIN
INSERT INTO history (dt, tname, cuser, id, op)
SELECT
getdate(),
'Cities',
ldu,
cityId,
case when exists (select 1 from deleted) then 'Update' else 'Insert' end
FROM inserted;
END
另一方面,如果您想同时记录“旧”行和“新”行(这不是您的代码所做的,即使是单行更新),那么您想要 union all
来自 inserted
和 deleted
.
的两个查询 select
您正在交叉连接 inserted
和 deleted
。通常,它们将使用 table 的主键连接,大概是 CityId
:
INSERT INTO history ([dt], [tname], [cuser] , [id], op)
SELECT GETDATE(), 'Cities', i.ldu, i.CityId, @operation
FROM deleted d JOIN
inserted i
ON d.CityId = i.CityId;
在这种情况下,deleted
未被使用,因此甚至不需要包含在查询中。
您可以使用 LEFT JOIN
:
在 table 中将整个触发器实现为单个查询
INSERT INTO history ([dt], [tname], [cuser] , [id], op)
SELECT GETDATE(), 'Cities', i.ldu, i.CityId,
(CASE WHEN d.CityId IS NOT NULL THEN 'Update' ELSE 'Insert' END)
FROM inserted i LEFT JOIN
deleted d
ON d.CityId = i.CityId;
我的所有表都有用于 CRUD 操作的触发器。 这是一个示例:
ALTER TRIGGER [dbo].[Cities_tr] ON [dbo].[Cities] AFTER INSERT, UPDATE
AS
BEGIN
DECLARE @operation CHAR(6)
SET @operation = CASE WHEN EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
THEN 'Update'
WHEN EXISTS (SELECT * FROM inserted)
THEN 'Insert'
WHEN EXISTS(SELECT * FROM deleted)
THEN 'Delete'
ELSE NULL
END
IF @operation = 'Insert'
INSERT INTO history ([dt],[tname],[cuser] ,[id],op)
SELECT GETDATE(),'Cities', i.ldu, i.CityId,@operation
FROM inserted i
set nocount on
IF @operation = 'Update'
INSERT INTO history ([dt],[tname],[cuser] ,[id],op)
SELECT GETDATE(),'Cities', i.ldu, i.CityId,@operation
FROM deleted d, inserted i
END
如果我更新一行,一切正常,触发器会在历史记录中插入一行。
例如
update top(1) cities set f=1
但是如果更新了不止一行,将插入updatedrow^2行。
例如 9 表示 3 行 100 表示 10 行...
我的触发器出了什么问题,我该如何解决?
您的代码存在问题,您正在交叉连接 inserted
和 deleted
。在多行更新中,两者都包含许多行,笛卡尔积相乘。
您似乎真的想记录“新”行(插入或更新)。如果是这样,您不想从 deleted
select。此外,条件逻辑可以在单个查询中移动,这样可以简化您的代码,如下所示:
ALTER TRIGGER dbo.Cities_tr
ON dbo.Cities
AFTER INSERT, UPDATE
AS
BEGIN
INSERT INTO history (dt, tname, cuser, id, op)
SELECT
getdate(),
'Cities',
ldu,
cityId,
case when exists (select 1 from deleted) then 'Update' else 'Insert' end
FROM inserted;
END
另一方面,如果您想同时记录“旧”行和“新”行(这不是您的代码所做的,即使是单行更新),那么您想要 union all
来自 inserted
和 deleted
.
您正在交叉连接 inserted
和 deleted
。通常,它们将使用 table 的主键连接,大概是 CityId
:
INSERT INTO history ([dt], [tname], [cuser] , [id], op)
SELECT GETDATE(), 'Cities', i.ldu, i.CityId, @operation
FROM deleted d JOIN
inserted i
ON d.CityId = i.CityId;
在这种情况下,deleted
未被使用,因此甚至不需要包含在查询中。
您可以使用 LEFT JOIN
:
INSERT INTO history ([dt], [tname], [cuser] , [id], op)
SELECT GETDATE(), 'Cities', i.ldu, i.CityId,
(CASE WHEN d.CityId IS NOT NULL THEN 'Update' ELSE 'Insert' END)
FROM inserted i LEFT JOIN
deleted d
ON d.CityId = i.CityId;