触发插入 table 中的数据保留以及在一列上更新和插入的组合

Triggers data retention in inserted table and combination of update and insert on one column

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

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

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

  2. 多个用户同时提交数据。数据在插入的table中停留了多长时间?是否有可能如果两个用户提交数据,触发器将被触发两次,因此这些行将被复制两次到快照table?

更新触发后:

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

是的,你可以组合触发器,只需使用 LEFT JOIN 到内存驻留 table deleted,并更改你的 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)

关于你的第二个问题,你所描述的是不可能的,两个用户不能同时进行相同的更新,即使只是相隔几微秒,它仍然会先做一个再做另一个(每个会有它自己的 inserted/deleted table),所以是的,这总是会触发触发器两次。

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

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

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

  • 请注意在子查询的第一部分使用虚拟 table(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