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 - 您应该使用 inserteddeleted

想一想如何根据已插入和已删除的 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() 函数

这是学校的问题吗?因为触发器确实是最后的手段,但人们仍然坚持在学校教授它们,即使它们大多无关紧要。

什么是相关是我在此处发布的基于集合的解决方案