使用“ AFTER INSERT,UPDATE,DELETE” DML触发器。如何引用触发行?

时间:2018-12-31 01:05:11

标签: sql sql-server tsql triggers database-trigger

我有一个主表,我想在其中汇总子表中各行的值。一个或多个子行将引用一个主行。我希望Main.Total是所有对应的Sub.Amount值的总和。我正在尝试实现AFTER INSERT,UPDATE,DELETE触发器来完成此操作。 我愿意接受触发器以外的其他解决方案,例如使用视图,如果这是一种更好的解决方案。即使有更好的解决方案,我仍然有兴趣学习如何通过触发器解决此问题,即使仅出于学术目的。这是一个简化的示例:

CREATE DATABASE TEST;
GO
USE TEST;
GO

CREATE TABLE Main (
    Id INT
    ,Total INT DEFAULT(0)
    );
GO

CREATE TABLE Sub (
    Main_fk INT
    ,Sub_Id INT
    ,Amount INT
    );
GO

CREATE TRIGGER Update_Main_Total
    ON Sub
    AFTER INSERT, UPDATE, DELETE
    AS BEGIN
        DECLARE @Main_Id INT = (
            --  The Main_fk value for the inserted/updated/deleted
            --  Sub row that caused the trigger to fire
            );
        UPDATE Main
        SET Total = (
            SELECT SUM(Amount)
            FROM Sub
            WHERE Main_fk = @Main_Id
            )
        WHERE Id = @Main_Id;
    END;
GO

2 个答案:

答案 0 :(得分:2)

SQL Server中没有for-each-row触发器。相反,您可以在触发器中访问inserteddeleted伪表。 inserted包含要插入的行或行的更改版本,以及deleted删除的行或更新前的行版本。

因此,您需要使用类似以下的内容来更新总和。它首先获取inserted的每个ID的总和,并deleted的模拟的每个ID的总和,然后对结果进行完全连接-这里需要完全连接,因为并非所有ID都在这两个集合中,所以需要完全连接-然后更新{{ 1}}。

main

(如果您没有UPDATE m SET m.total = m.total + z.total FROM main m INNER JOIN (SELECT coalesce(x.main_fk, y.main_fk) main_fk, coalesce(x.total, 0) - coalesce(y.total, 0) total FROM (SELECT i.main_fk, sum(i.amount) total FROM inserted i GROUP BY i.main_fk) x FULL JOIN (SELECT d.main_fk, sum(d.amount) total FROM delete d GROUP BY d.main_fk) y ON y.main_fk = x.main_fk) z ON z.main_fk = m.id; 条记录,您可能想将total中的main设置为NULL。上面的查询不会这样做,这需要一些额外的工作。)

但是,物理地存储可以从其他人那里计算出的数字会带来不一致的风险。如果触发器在一段时间内被禁用或由于其他原因(适当)无法正常工作,则sub表中的更改不会(正确)反映在sub表中。您那里可能有虚假数字,甚至可能无法识别。

如果可能的话,我会避免这样的事情,而是选择一个视图。 (写起来也更容易。))

main

(这里您将获得CREATE VIEW main_with_total AS SELECT m.id, sum(s.amount) total FROM main m LEFT JOIN sub s ON s.main_fk = m.id GROUP BY m.id; 作为NULL的ID,其中不存在任何total记录。如果要0,请将sub的表达式更改为{{ 1}}。)

答案 1 :(得分:-2)

我很容易就能弄清楚。感谢@Dale_Burrell在注释中的指导。我发现此链接是我想要的信息的更直接答案:https://www.mssqltips.com/sqlservertip/2342/understanding-sql-server-inserted-and-deleted-tables-for-dml-triggers/

简而言之,我需要引用内置的inserteddeleted表来引用触发行。我的问题是立即“但是更新呢?”。更新将使用这两个表(考虑一下,或使用上面的链接进行解释)。这是更正后的触发器:

CREATE TRIGGER Update_Main_Total
    ON Sub
    AFTER INSERT, UPDATE, DELETE
    AS BEGIN
        DECLARE @main_id INT;

        IF EXISTS (SELECT * FROM inserted)
            SELECT @main_id = Main_fk
            FROM inserted;

        ELSE
            SELECT @main_id = Main_fk
            FROM deleted;

        UPDATE Main
        SET Total = (
            SELECT SUM(Amount)
            FROM Sub
            WHERE Main_fk = @Main_Id
            )
        WHERE Id = @Main_Id;
    END;
GO