从插入触发器更新时更新触发器后不触发

Posted

技术标签:

【中文标题】从插入触发器更新时更新触发器后不触发【英文标题】:Not firing after update trigger when updating from insert trigger 【发布时间】:2016-08-16 11:59:22 【问题描述】:

我在一张表上有两个触发器,一个在插入时触发并使用来自其他表的附加信息更新该行

CREATE TRIGGER [dbo].[eTteamTg]
ON  [dbo].[entryTable]  
AFTER INSERT 
AS  
BEGIN 
    UPDATE entryTable
    SET shiftTeam = (SELECT TOP 1 shiftTeamMemb.teamId 
                     FROM shiftTeamMemb 
                     WHERE shiftTeamMemb.personalNumber = i.personalNumber)
    FROM entryTable
    INNER JOIN inserted i ON i.ID = entryTable.ID
END  

第二个在更新后触发

CREATE TRIGGER [dbo].[eThistoryUpdTg] ON  [dbo].[entryTable]  
AFTER update
AS  
BEGIN
    INSERT INTO eThistory(my columns)
        SELECT
            *, HOST_Name() + ' ' + SUSER_NAME() + ' Upd', GETDATE() 
        FROM
            deleted
END

当我使用AFTER INSERT 触发器更新行时,我需要做的是不触发 AFTER UPDATE 触发器。有可能吗?

【问题讨论】:

您的insert 触发器似乎已损坏。子查询是不相关的(它引入了对inserted 的单独引用,而不是我猜你正在尝试做的,引用i),因此多行插入将产生一个错误,类似于“子查询返回更多超过一个值”。 哦,我现在明白了,更新了问题@Damien_The_Unbeliever 对不起,我错了。它不会返回那个错误。相反,它会默默地破坏您的数据,因为您已经使用top 1 强制它这样做了。同样,该子查询是不相关的。因此,如果 inserted 包含 多个 行,其中 不同 personalNumber 值,这些值中的 一个 将成功导致在 @987654331 中发生查找@ 然后 每一 行最初插入将收到该值。 使用TOP 1 没有明确的ORDER BY 是没有意义的——你希望得到哪一行?由于没有ORDER BY,因此无法保证任何排序 - 你基本上得到一个 任意 行 .... 这真的是你要找的吗?? 我同意你的观点,这个子查询写得不好,我稍后会重写它,现在我需要解决我在这个问题中的主要问题。 【参考方案1】:

我建议你将这两个触发器合并为一个:

CREATE TRIGGER [dbo].[eTteamTg]
ON  [dbo].[entryTable]  
AFTER INSERT, UPDATE
AS  
BEGIN 
    UPDATE entryTable
    SET shiftTeam = (SELECT TOP 1 shiftTeamMemb.teamId 
                     FROM shiftTeamMemb 
                     WHERE shiftTeamMemb.personalNumber = i.personalNumber)
    FROM entryTable
    INNER JOIN inserted i ON i.ID = entryTable.ID

    INSERT INTO eThistory(my columns)
    SELECT
        *, HOST_Name() + ' ' + SUSER_NAME() + ' Upd', GETDATE() 
    FROM
        deleted
END  

那么在INSERT之后,会更新shiftTeam,不会使用`deleted1。

在 UPDATE 之后,它将更新 shiftTeam 并使用 deleted 来获取以前的(甚至是以上更新的)值。

或者如果您不需要更新 shiftTeam 以防在此表上进行 UPDATE,您可以添加 IF 语句:

CREATE TRIGGER [dbo].[eTteamTg]
ON  [dbo].[entryTable]  
AFTER INSERT, UPDATE
AS  
BEGIN 

    IF (SELECT COUNT(*) FROM deleted) = 0 --That means it was INSERT
    BEGIN
        UPDATE entryTable
        SET shiftTeam = (SELECT TOP 1 shiftTeamMemb.teamId 
                         FROM shiftTeamMemb 
                         WHERE shiftTeamMemb.personalNumber = i.personalNumber)
        FROM entryTable
        INNER JOIN inserted i ON i.ID = entryTable.ID
    END

    INSERT INTO eThistory(my columns)
    SELECT
        *, HOST_Name() + ' ' + SUSER_NAME() + ' Upd', GETDATE() 
    FROM
        deleted
END  

【讨论】:

太棒了!这正是我所需要的,我不知道您可以对更多操作执行相同的触发器。这种方式相当巧妙。我必须使用第二种方式,因为我只需要在插入时更新它【参考方案2】:

像这样实现你的第二个触发器,

CREATE TRIGGER [dbo].[eThistoryUpdTg] ON [dbo].[entryTable]
AFTER UPDATE
AS
BEGIN
    INSERT INTO eThistory (my columns)
    SELECT *
        ,HOST_Name() + ' ' + SUSER_NAME() + ' Upd'
        ,GETDATE()
    FROM deleted d
    WHERE NOT EXISTS (
            SELECT TOP 1 shiftTeamMemb.teamId
            FROM shiftTeamMemb
            INNER JOIN inserted I ON i.personalNumber = shiftTeamMemb.personalNumber
            INNER JOIN entryTable E ON E.ID = I.ID
            WHERE shiftTeamMemb.personalNumber = i.personalNumber
            )
END

【讨论】:

以上是关于从插入触发器更新时更新触发器后不触发的主要内容,如果未能解决你的问题,请参考以下文章

触发器中的 ORACLE SQL 更新语句在 2 行后不起作用

更新触发器如何删除旧记录

SQL Server 触发器插入/更新特定列

oracle触发器在表中插入新行时更新新的视图行

插入更新另一个表后创建触发器

Oracle 触发器在插入或更新时更新字段