SQL 服务器:删除触发器不起作用
SQL Server : delete trigger not working
下面是table Country
:
+------------------+-----------+
| COLUMN_NAME | DATA_TYPE |
+------------------+-----------+
| ID | int |
| Name | varchar |
| CapitalCity | varchar |
| Population | int |
| OfficialReligion | varchar |
| OfficialLanguage | varchar |
| DateEntered | datetime |
+------------------+-----------+
然后我有另一个非常相似的 table,称为 CountryRecord
,它具有相同的列,然后还有两个新的列:
+-------------------------+-----------+
| COLUMN_NAME | DATA_TYPE |
+-------------------------+-----------+
| ID | int |
| OriginalID | int |
| Name | varchar |
| CapitalCity | varchar |
| Population | int |
| OfficialReligion | varchar |
| OfficialLanguage | varchar |
| DateEntered | datetime |
| QueryType | varchar |
+-------------------------+-----------+
在Country
中插入、更新或删除数据时,会调用一个触发器,该触发器也会在CountryRecord
中插入行,目的是记录对哪行数据进行了什么查询。触发器如下:
ALTER TRIGGER [dbo].[CountryRecord_Trigger]
ON [dbo].[Country]
AFTER UPDATE, INSERT, DELETE
AS
DECLARE @CountryID INT, @queryType VARCHAR(20);
--Update trigger
IF EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
BEGIN
SET @queryType = 'UPDATE';
SELECT @CountryID = ID FROM inserted i;
INSERT INTO CountryRecord (OriginalID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, QueryType)
SELECT
ID, Name, CapitalCity, Population, OfficialReligion,
OfficialLanguage, DateEntered, @queryType
FROM
Country
WHERE
@CountryID = ID;
END
--Insert trigger
IF EXISTS (SELECT * FROM inserted) AND NOT EXISTS (SELECT * FROM deleted)
BEGIN
SET @queryType = 'INSERT';
SELECT @CountryID = ID FROM inserted i;
INSERT INTO CountryRecord (OriginalID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, QueryType)
SELECT
ID, Name, CapitalCity, Population, OfficialReligion,
OfficialLanguage, DateEntered, @queryType
FROM
Country
WHERE
@CountryID = ID;
END
--Delete trigger
IF NOT EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
BEGIN
SET @queryType = 'DELETE';
SELECT @CountryID = ID FROM deleted d;
INSERT INTO CountryRecord (OriginalID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, QueryType)
SELECT
ID, Name, CapitalCity, Population, OfficialReligion,
OfficialLanguage, DateEntered, @queryType
FROM
Country
WHERE
@CountryID = ID;
END
但是,虽然插入和更新触发器有效,但删除触发器无效。当我 运行 一个删除查询时,我只选择一行,所以我很确定删除查询不会 select 超过一行(相反,看起来是 select什么都没有,因为在删除查询 运行 之后检查 CountryRecord
的当前标识显示它没有增加一个)。
更新: 将删除触发器更改为使用 ELSE
而不是满足条件集并不能解决问题。相反,任何更新查询都会调用更新和删除触发器,而删除查询仍然无效。
更新 2: 只是为了检查我是否只影响了一行,我为每个触发器添加了 select 查询以及 [=21 的打印=] - select 查询仅显示 1 行,而 @CountryID
没有出现 null/blank。我还尝试更改列,思考 NOT NULL 约束是否有任何影响,但没有。
更新 3: 我现在可以使用 Delete,尽管它只适用于单行删除。多行删除不能正常工作 - 例如,如果我删除两条记录,只有一行进入审计 table。我还更新了 Insert 和 Update 以使用类似的代码结构,因此它们也有相同的多行问题。删除部分的代码片段:
SET @queryType = 'DELETE';
SELECT CountryID = ID FROM deleted;
INSERT INTO CountryRecord (OriginalID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, QueryType)
SELECT ID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, @queryType FROM deleted WHERE @CountryID = ID;
DECLARE @auditID int;
SET @auditID = SCOPE_IDENTITY();
UPDATE CountryRecord SET QueryType = @queryType WHERE ID = @auditID;
您的删除不起作用的原因是记录已从 country
table 中删除。所以你不能 select 它离开那里。
您需要从 deleted
table 获取它。
您的其他两个查询还引用了触发器中的 Country
table,这是个坏主意。您不应该在查询中使用来源 table - 您应该使用 inserted
和 deleted
。
想一想如何根据已插入和已删除的 table 编写一个查询,它可以在一个查询中执行您想要的所有操作,而不管插入和删除的记录有多少。如果你不放弃这个问题(它经常发生),我会在这个答案中添加一个例子。
这是一个一次性保存您的插入和更新的查询:
INSERT INTO CountryRecord (
Name,
CapitalCity,
Population,
OfficialReligion,
OfficialLanguage,
DateEntered,
QueryType)
SELECT
I.Name,
I.CapitalCity,
I.Population,
I.OfficialReligion,
I.OfficialLanguage,
GETDATE() As DateEntered,
CASE
WHEN EXISTS(SELECT * FROM DELETED D WHERE D.ID = I.ID)
THEN 'U' ELSE 'I'
END As QueryType
FROM INSERTED I
我漏掉了 OriginalID
因为人们你不应该更新主键,此外,在这种情况下不可能计算出更新了什么。您只能在 BEFORE 触发器中执行此操作
这是一个捕获您删除的内容。我已经分别完成了这些,因此它们更容易理解,并且对于大型记录集可能表现得更好
INSERT INTO CountryRecord (
Name,
CapitalCity,
Population,
OfficialReligion,
OfficialLanguage,
DateEntered,
QueryType)
SELECT
D.Name,
D.CapitalCity,
D.Population,
D.OfficialReligion,
D.OfficialLanguage,
GETDATE() As DateEntered,
'D' As QueryType
FROM DELETED D
WHERE NOT EXISTS (SELECT * FROM INSERTED I WHERE I.ID = D.ID)
无论更改了多少行,这些都将起作用
如果你想实际检查更新中更改了哪些列,你可以再次在 select 语句中单独比较它们,或者你可以使用 UPDATED()
函数
这是学校的问题吗?因为触发器确实是最后的手段,但人们仍然坚持在学校教授它们,即使它们大多无关紧要。
什么是相关是我在此处发布的基于集合的解决方案
下面是table Country
:
+------------------+-----------+
| COLUMN_NAME | DATA_TYPE |
+------------------+-----------+
| ID | int |
| Name | varchar |
| CapitalCity | varchar |
| Population | int |
| OfficialReligion | varchar |
| OfficialLanguage | varchar |
| DateEntered | datetime |
+------------------+-----------+
然后我有另一个非常相似的 table,称为 CountryRecord
,它具有相同的列,然后还有两个新的列:
+-------------------------+-----------+
| COLUMN_NAME | DATA_TYPE |
+-------------------------+-----------+
| ID | int |
| OriginalID | int |
| Name | varchar |
| CapitalCity | varchar |
| Population | int |
| OfficialReligion | varchar |
| OfficialLanguage | varchar |
| DateEntered | datetime |
| QueryType | varchar |
+-------------------------+-----------+
在Country
中插入、更新或删除数据时,会调用一个触发器,该触发器也会在CountryRecord
中插入行,目的是记录对哪行数据进行了什么查询。触发器如下:
ALTER TRIGGER [dbo].[CountryRecord_Trigger]
ON [dbo].[Country]
AFTER UPDATE, INSERT, DELETE
AS
DECLARE @CountryID INT, @queryType VARCHAR(20);
--Update trigger
IF EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
BEGIN
SET @queryType = 'UPDATE';
SELECT @CountryID = ID FROM inserted i;
INSERT INTO CountryRecord (OriginalID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, QueryType)
SELECT
ID, Name, CapitalCity, Population, OfficialReligion,
OfficialLanguage, DateEntered, @queryType
FROM
Country
WHERE
@CountryID = ID;
END
--Insert trigger
IF EXISTS (SELECT * FROM inserted) AND NOT EXISTS (SELECT * FROM deleted)
BEGIN
SET @queryType = 'INSERT';
SELECT @CountryID = ID FROM inserted i;
INSERT INTO CountryRecord (OriginalID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, QueryType)
SELECT
ID, Name, CapitalCity, Population, OfficialReligion,
OfficialLanguage, DateEntered, @queryType
FROM
Country
WHERE
@CountryID = ID;
END
--Delete trigger
IF NOT EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
BEGIN
SET @queryType = 'DELETE';
SELECT @CountryID = ID FROM deleted d;
INSERT INTO CountryRecord (OriginalID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, QueryType)
SELECT
ID, Name, CapitalCity, Population, OfficialReligion,
OfficialLanguage, DateEntered, @queryType
FROM
Country
WHERE
@CountryID = ID;
END
但是,虽然插入和更新触发器有效,但删除触发器无效。当我 运行 一个删除查询时,我只选择一行,所以我很确定删除查询不会 select 超过一行(相反,看起来是 select什么都没有,因为在删除查询 运行 之后检查 CountryRecord
的当前标识显示它没有增加一个)。
更新: 将删除触发器更改为使用 ELSE
而不是满足条件集并不能解决问题。相反,任何更新查询都会调用更新和删除触发器,而删除查询仍然无效。
更新 2: 只是为了检查我是否只影响了一行,我为每个触发器添加了 select 查询以及 [=21 的打印=] - select 查询仅显示 1 行,而 @CountryID
没有出现 null/blank。我还尝试更改列,思考 NOT NULL 约束是否有任何影响,但没有。
更新 3: 我现在可以使用 Delete,尽管它只适用于单行删除。多行删除不能正常工作 - 例如,如果我删除两条记录,只有一行进入审计 table。我还更新了 Insert 和 Update 以使用类似的代码结构,因此它们也有相同的多行问题。删除部分的代码片段:
SET @queryType = 'DELETE';
SELECT CountryID = ID FROM deleted;
INSERT INTO CountryRecord (OriginalID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, QueryType)
SELECT ID, Name, CapitalCity, Population, OfficialReligion, OfficialLanguage, DateEntered, @queryType FROM deleted WHERE @CountryID = ID;
DECLARE @auditID int;
SET @auditID = SCOPE_IDENTITY();
UPDATE CountryRecord SET QueryType = @queryType WHERE ID = @auditID;
您的删除不起作用的原因是记录已从 country
table 中删除。所以你不能 select 它离开那里。
您需要从 deleted
table 获取它。
您的其他两个查询还引用了触发器中的 Country
table,这是个坏主意。您不应该在查询中使用来源 table - 您应该使用 inserted
和 deleted
。
想一想如何根据已插入和已删除的 table 编写一个查询,它可以在一个查询中执行您想要的所有操作,而不管插入和删除的记录有多少。如果你不放弃这个问题(它经常发生),我会在这个答案中添加一个例子。
这是一个一次性保存您的插入和更新的查询:
INSERT INTO CountryRecord (
Name,
CapitalCity,
Population,
OfficialReligion,
OfficialLanguage,
DateEntered,
QueryType)
SELECT
I.Name,
I.CapitalCity,
I.Population,
I.OfficialReligion,
I.OfficialLanguage,
GETDATE() As DateEntered,
CASE
WHEN EXISTS(SELECT * FROM DELETED D WHERE D.ID = I.ID)
THEN 'U' ELSE 'I'
END As QueryType
FROM INSERTED I
我漏掉了 OriginalID
因为人们你不应该更新主键,此外,在这种情况下不可能计算出更新了什么。您只能在 BEFORE 触发器中执行此操作
这是一个捕获您删除的内容。我已经分别完成了这些,因此它们更容易理解,并且对于大型记录集可能表现得更好
INSERT INTO CountryRecord (
Name,
CapitalCity,
Population,
OfficialReligion,
OfficialLanguage,
DateEntered,
QueryType)
SELECT
D.Name,
D.CapitalCity,
D.Population,
D.OfficialReligion,
D.OfficialLanguage,
GETDATE() As DateEntered,
'D' As QueryType
FROM DELETED D
WHERE NOT EXISTS (SELECT * FROM INSERTED I WHERE I.ID = D.ID)
无论更改了多少行,这些都将起作用
如果你想实际检查更新中更改了哪些列,你可以再次在 select 语句中单独比较它们,或者你可以使用 UPDATED()
函数
这是学校的问题吗?因为触发器确实是最后的手段,但人们仍然坚持在学校教授它们,即使它们大多无关紧要。
什么是相关是我在此处发布的基于集合的解决方案