【问题标题】:SQL Server : delete trigger not workingSQL Server:删除触发器不起作用
【发布时间】:2018-05-07 02:21:01
【问题描述】:

下表为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

但是,虽然插入和更新触发器起作用,但删除触发器却不起作用。当我运行删除查询时,我只选择一行,所以我相当确定删除查询没有选择多于一行(相反,它似乎什么也没选择,因为检查了@987654328 的当前身份@ 运行删除查询后显示它没有增加一)。

更新:将删除触发器更改为使用 ELSE 而不是满足条件集并不能解决问题。相反,任何更新查询都会调用更新和删除触发器,而删除查询仍然无效。

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

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

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;

【问题讨论】:

  • 您正在犯第一个触发错误:假设 INSERTEDDELETED 只有一行 - 他们没有。此外,您的审计表可能不需要所有这些列
  • 我在INSERTEDDELETED 的选择查询中添加了 - 结果只显示了 1 行。目前我所做的只是插入、更新和删除具有相同 ID 的同一行。我还做了一个PRINT@CountryID - 在每个触发器中,@CountryID 不是空/空白。
  • 这很好,但您应该尝试修复已经存在其他错误的代码。如果你得到这个工作,你仍然需要修复多行错误。您想要做的可以在 one 插入语句中实现。 stackoverflow上有很多例子。我会找到一个
  • 您的删除不起作用的原因是该记录已从country 表中删除。您需要从 deleted 表中获取它。你的另外两个也一样。在触发器中引用源表是个坏主意。
  • 想一想如何从inserteddeleted 表中编写一个查询,在一个查询中完成您想要的所有操作,无论inserted 中有多少记录和deleted

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


【解决方案1】:

您的删除不起作用的原因是该记录已从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() 函数

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

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

【讨论】:

  • 这是有道理的。虽然我也有一个问题,即我有额外的列 queryType 不在 INSERTEDDELETED 中 - 我正在努力与其他列一起获得该值。
  • 是的,您也可以在一个查询中做到这一点
  • 我假设您指的是MERGE 功能。有一件事 - 对于UPDATE,您是否需要在条件中指定源和目标之间的每一列可能不一样?例如。 WHEN MATCHED AND Target.Name <> Source.Name OR Target.CapitalCity <> Source.CapitalCity 等等。对于较大的表,这将是相当有问题的。
  • 您只是在审计表中插入行,所以merge 是不必要的(通常是不必要的)。您只需要正确获取select 语句并将其直接插入您的审计表中。稍后我会根据建议修改我的问题。
  • 不敢相信我没有早点意识到这个解决方案......更糟糕的是我之前已经使用过很多次了。从一张表复制到另一张表的方法与从INSERTEDDELETED 复制的方法相同,我只是没能把两个和两个放在一起。非常感谢你帮助我意识到我的心不在焉。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多