【问题标题】:Triggers data retention in inserted table and combination of update and insert on one column触发插入表中的数据保留以及在一列上更新和插入的组合
【发布时间】:2021-09-29 21:56:36
【问题描述】:

我在 SQL Server 中有一个表,其中包含所有数据的快照,以跟踪源表 tbl_D_project 的状态变化。如果此状态发生变化或在tbl_D_project 中添加了新记录,则应启动触发器并将该行复制到快照表中。

我根据在这里找到的另一个问题编写了两个触发器。一个触发器用于更新,一个触发器用于插入。我的问题是:

  1. 有没有更有效的方法?我可以以某种方式将两者合二为一,所以插入后更新?

  2. 多个用户同时提交数据。数据在插入的表中保留多长时间?有没有可能如果两个用户提交数据,触发器会被触发两次,所以行会被复制两次到快照表中?

更新触发后:

ALTER TRIGGER [dbo].[tr_copysnapshot_update]
ON [CDB].[dbo].[tbl_D_project]
AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON
  
    INSERT INTO cbdbpc.dbo.[Snapshot_tbl_D_project]
        (Snapshot_date, Opp_ID, Reference_ID, Project_ID, Project_opportunity, [State], [Status])
        SELECT
            GETDATE(), S.Opp_ID, S.Reference_ID, S.Project_ID, 
            S.Project_opportunity, S.[State], S.[Status]
        FROM 
            cbdbpc.dbo.[tbl_D_project] S
        INNER JOIN 
            Inserted I ON s.opp_id = I.Opp_ID
        INNER JOIN 
            Deleted D ON s.opp_ID = D.Opp_ID
        WHERE 
            D.[Status] <> I.[Status]
END

插入触发器后:

ALTER TRIGGER [dbo].[tr_copysnapshot_insert]
ON [CBD].[dbo].[tbl_D_project]
AFTER INSERT
AS 
BEGIN
    SET NOCOUNT ON
  
    INSERT INTO cbdbpc.dbo.[Snapshot_tbl_D_project]
        (Snapshot_date, Opp_ID, Reference_ID, Project_ID, 
         Project_opportunity, [State], [Status])
        SELECT
            GETDATE(), S.Opp_ID, S.Reference_ID, S.Project_ID, 
            S.Project_opportunity, S.[State], S.[Status]
        FROM 
            cbdbpc.dbo.[tbl_D_project] S
        INNER JOIN 
            Inserted I ON s.opp_id = I.Opp_ID
        WHERE 
            S.[Opp_ID] = I.[Opp_ID]
END 

SQL update trigger only when column is modified

【问题讨论】:

    标签: sql sql-server triggers


    【解决方案1】:

    是的,您可以组合触发器,只需对内存驻留表deleted 使用LEFT JOIN,并更改您的where 子句,以便仅在状态更改(更新)或没有记录时添加一行删除(插入),即

    INSERT INTO cbdbpc.dbo.[Snapshot_tbl_D_project]
        (Snapshot_date, Opp_ID, Reference_ID, Project_ID, Project_opportunity, [State], [Status])
    SELECT
        GETDATE(), S.Opp_ID, S.Reference_ID, S.Project_ID, 
        S.Project_opportunity, S.[State], S.[Status]
    FROM 
        cbdbpc.dbo.[tbl_D_project] S
    INNER JOIN 
        Inserted I ON s.opp_id = I.Opp_ID
    LEFT JOIN 
        Deleted D ON s.opp_ID = D.Opp_ID
    WHERE 
        (D.[Status] <> I.[Status] OR d.Opp_ID IS NULL)
    

    对于您的第二个问题,您所描述的是不可能的,两个用户不能同时进行相同的更新,即使相隔仅几微秒,它仍然会先做一个然后另一个(每个用户都会有它是自己的插入/删除表),所以是的,这总是会触发触发器两次。

    如果两个用户都执行相同的更新,那么您的 where 子句规定状态必须已更改将阻止第二次插入。如果您想扩展它以在其他列发生更改时捕获记录,但如果没有任何更改,则可以使用以下 WHERE 子句:

    WHERE NOT EXISTS (SELECT i.* INTERSECT SELECT d.*)
    

    【讨论】:

    • 谢谢!很高兴知道每个更新都有自己的插入/删除表,所以没有问题。
    【解决方案2】:

    通常我不建议组合触发器,因为代码通常完全不同。但在这种情况下,代码很简单。

    • 请注意在子查询的第一部分中使用了虚拟表(SELECT,没有 FROM)。
    • 我们总是需要比较主键,所以我们把它放到子查询中
    CREATE OR ALTER TRIGGER [dbo].[tr_copysnapshot]
    ON [CDB].[dbo].[tbl_D_project]
    AFTER INSERT, UPDATE
    AS 
    
        SET NOCOUNT ON;
      
        INSERT INTO cbdbpc.dbo.[Snapshot_tbl_D_project]
            (Snapshot_date, Opp_ID, Reference_ID, Project_ID, Project_opportunity, [State], [Status])
            SELECT
                GETDATE(), S.Opp_ID, S.Reference_ID, S.Project_ID, 
                S.Project_opportunity, S.[State], S.[Status]
            FROM 
                cbdbpc.dbo.[tbl_D_project] S
            INNER JOIN 
                Inserted I ON S.Opp_id = I.Opp_ID
            WHERE NOT EXISTS (
                SELECT I.Opp_ID, I.[Status]
                INTERSECT
                SELECT D.Opp_ID, D.[Status]
                FROM deleted d);
    
    
    GO
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-06
      • 2015-05-24
      • 2017-12-18
      • 2021-10-15
      • 2012-07-26
      • 1970-01-01
      • 2014-03-08
      相关资源
      最近更新 更多