SQL Server:删除触发器不起作用

时间:2018-05-07 02:21:02

标签: sql sql-server triggers sql-server-2014 sql-delete

下面是表格Country

+------------------+-----------+
|   COLUMN_NAME    | DATA_TYPE |
+------------------+-----------+
| ID               | int       |
| Name             | varchar   |
| CapitalCity      | varchar   |
| Population       | int       |
| OfficialReligion | varchar   |
| OfficialLanguage | varchar   |
| DateEntered      | datetime  |
+------------------+-----------+

然后我有一个名为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

但是,当Insert和Update触发器工作时,Delete触发器不起作用。当我运行删除查询时,我只选择一行,所以我很确定删除查询不会选择多行(相反,似乎它没有选择任何内容,因为检查{{的当前标识) 1}}运行删除查询后显示它没有增加一个。)

更新:更改删除触发器以使用CountryRecord而不是满足条件设置无法解决问题。相反,任何更新查询都将同时调用更新和删除触发器,而删除查询仍然无效。

更新2:为了检查我是否只影响了一行,我添加了针对每个触发器的选择查询以及ELSE的打印 - 选择查询仅显示1行,而@CountryID没有出现为空/空白。我也尝试改变列,考虑NOT NULL约束是否有任何影响,但它没有。

更新3:我现在有删除工作,虽然它只适用于单行删除。多行删除无法正常工作 - 例如,如果删除两条记录,则只有一行进入审计表。我还更新了插入和更新以使用类似的代码结构,因此它们也具有相同的多行问题。删除部分的代码段:

@CountryID

1 个答案:

答案 0 :(得分:2)

删除不起作用的原因是因为该记录已从country表中删除。所以你不能在那里选择它。

您需要从deleted表中获取它。

您的其他两个查询也会引用触发器中的Country表,这是个坏主意。您不应在查询中使用源表 - 您应该使用inserteddeleted代替。

考虑如何在一个查询中从插入和删除的表中编写一个查询,无论插入和删除多少条记录,都可以编写一个查询。如果你不放弃这个问题(它发生了很多),我会在这个答案中加上一个例子。

以下是一次性保存插入和更新的查询:

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()函数

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

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