Sql 服务器触发器触发平方次数
Sql Server Trigger Fires Square Number of Times
我正在尝试将触发器添加到 table 以记录某些行的更改值。一切都按预期工作,除了触发器执行行数的平方次。例如,如果我要在批量查询中更改 table 中的 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
@Larnu 是正确的,您的连接语法不是最理想的,但它向您隐藏了潜在的问题(问题不在于触发器执行了很多次)。
您当前的逻辑匹配 任何名称不同的行。如果您更新两行,除非所有四个名称前后都相同,否则它们都会匹配。您需要匹配名称不同 但仅相同 Id
的任何行 。作为使用#temp 表和每种可能情况的示例(名称更改、名称不变、名称变为 NULL 或变为 NOT NULL,或名称保持为 NULL):
CREATE TABLE #deleted (Id int identity(1,1), name nvarchar(32));
CREATE TABLE #inserted(Id int identity(1,1), name nvarchar(32));
INSERT #deleted (name) VALUES('bob'),('frank'), (NULL), ('bla'),(NULL);
INSERT #inserted(name) VALUES('bob'),('franky'),('boop'),(NULL), (NULL);
-- not correct:
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)
-- correct:
SELECT i.Id, 'Name', d.Name, i.Name
FROM #deleted AS d
INNER JOIN #inserted AS i
ON d.Id = i.Id
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);
您需要加入触发器才能看起来更像后者。
此外,我建议始终在逻辑块周围使用 BEGIN
/END
。
IF something
BEGIN
do a bunch of stuff
END
...可能需要输入更多内容,但对于 reader 而言,它比...
更易于预测和直观
IF something
do a bunch of stuff
我正在尝试将触发器添加到 table 以记录某些行的更改值。一切都按预期工作,除了触发器执行行数的平方次。例如,如果我要在批量查询中更改 table 中的 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
@Larnu 是正确的,您的连接语法不是最理想的,但它向您隐藏了潜在的问题(问题不在于触发器执行了很多次)。
您当前的逻辑匹配 任何名称不同的行。如果您更新两行,除非所有四个名称前后都相同,否则它们都会匹配。您需要匹配名称不同 但仅相同 Id
的任何行 。作为使用#temp 表和每种可能情况的示例(名称更改、名称不变、名称变为 NULL 或变为 NOT NULL,或名称保持为 NULL):
CREATE TABLE #deleted (Id int identity(1,1), name nvarchar(32));
CREATE TABLE #inserted(Id int identity(1,1), name nvarchar(32));
INSERT #deleted (name) VALUES('bob'),('frank'), (NULL), ('bla'),(NULL);
INSERT #inserted(name) VALUES('bob'),('franky'),('boop'),(NULL), (NULL);
-- not correct:
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)
-- correct:
SELECT i.Id, 'Name', d.Name, i.Name
FROM #deleted AS d
INNER JOIN #inserted AS i
ON d.Id = i.Id
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);
您需要加入触发器才能看起来更像后者。
此外,我建议始终在逻辑块周围使用 BEGIN
/END
。
IF something
BEGIN
do a bunch of stuff
END
...可能需要输入更多内容,但对于 reader 而言,它比...
更易于预测和直观IF something
do a bunch of stuff