【发布时间】:2021-10-20 12:15:44
【问题描述】:
我正在尝试向记录更改的某些行的值的表添加触发器。一切都按预期工作,除了触发器执行行数的平方。例如,如果我要在批量查询中更改表中 5 个项目的名称,触发器将在 TriggerTable 中记录 25 个条目。每个项目都会记录更改的总项目数,因此第一个名称更改将被记录 5 次,因为总共有 5 次名称更改。在下面粘贴了触发器的一部分。
我尝试添加IF TRIGGER_NESTLEVEL() <= 1 没有成功。除了这部分之外,其余功能都很好用。任何建议表示赞赏!
CREATE OR ALTER TRIGGER [dbo].[Trigger]
ON [dbo].[Table]
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
IF UPDATE(Name)
IF EXISTS(SELECT Name FROM inserted) AND EXISTS(SELECT Name FROM deleted)
INSERT INTO TriggerTable (Id, ColumnName, OldValue, NewValue)
SELECT i.Id, 'Name', d.Name, i.Name
FROM deleted d, inserted i
WHERE d.Name <> i.Name OR (d.Name IS NULL AND i.Name IS NOT NULL) OR (d.Name IS NOT NULL AND i.Name IS NULL)
ELSE IF EXISTS(SELECT Name FROM inserted)
INSERT INTO TriggerTable (Id, ColumnName, NewValue)
SELECT i.Id, 'Name', i.Name
FROM inserted i
ELSE IF EXISTS(SELECT Name FROM deleted)
INSERT INTO TriggerTable (Id, ColumnName, OldValue)
SELECT d.Id, 'Name', d.Name
FROM deleted d
【问题讨论】:
-
"除了触发器执行行数的平方倍" 它不会“执行”那么多次,它会触发一次;每个 DML 语句一个触发器只执行一次,并不多。至于问题是因为你的 1980 年代的
CROSS JOIN在inserted和deleted上。我建议使用 1992INNER JOIN。 -
IF UPDATE不会像您认为的那样做。许多人误解了它实现的逻辑——一个可以理解但常见的错误。如果您的逻辑取决于对列值的实际更改,则您必须将旧值与新值进行比较以了解这一事实。对于插入语句,所有内容都会隐式更新。为什么您的触发器被定义为删除没有意义,但这是一个不同的问题。 -
考虑到逻辑上的差异,最好有单独的
INSERT和UPDATE触发器 -
@SMor 你说的是真的,但是我认为这里的使用是合适的——他们使用它来跳过任何不涉及该列的更新,并且至少对于更新案例他们尝试 只记录值发生变化的行(逻辑不太正确)。我不想考虑的是,是否有计划为表中的每一列创建这样的代码块。
-
@TCR 您是否考虑过更改跟踪、更改数据捕获、临时表或任何内置解决方案来阻止您完成所有这些繁重(且容易出错)的工作?
标签: sql sql-server database triggers