【问题标题】:More efficient way of logging changes in my table trigger在我的表触发器中记录更改的更有效方式
【发布时间】:2015-03-05 10:16:30
【问题描述】:

目前我对每个表的触发器对于表中的每个字段都是这样的:

ALTER TRIGGER [dbo].[trg_Statement] ON [dbo].[tbl_Statement]
FOR INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON
    INSERT INTO tbl_ChangeLog(TableName, ID, FieldName, OldValue, NewValue)
       SELECT  
          'Statement', CU.id, 'id', deleted.id,inserted.id
       FROM
          tbl_Statement CU
       LEFT JOIN 
          inserted on CU.id = inserted.id
       LEFT JOIN 
          deleted on CU.id = deleted.id
       WHERE
          (inserted.id is not null or deleted.id is not null)
          AND IsNull(inserted.id,'') <> IsNull(deleted.id,'')

    INSERT INTO tbl_ChangeLog(TableName, ID, FieldName, OldValue, NewValue)
       SELECT 
          'Statement', CU.id, 'idAccount', deleted.idAccount,inserted.idAccount
       FROM
          tbl_Statement CU
       LEFT JOIN 
          inserted on CU.id = inserted.id
       LEFT JOIN 
          deleted on CU.id = deleted.id
       WHERE
          (inserted.id is not null or deleted.id is not null)
          AND IsNull(inserted.idAccount,'') <> IsNull(deleted.idAccount,'')

    INSERT INTO tbl_ChangeLog(TableName, ID, FieldName, OldValue, NewValue)
       SELECT 
          'Statement', CU.id, 'OpeningBalance', deleted.OpeningBalance,inserted.OpeningBalance
       FROM
          tbl_Statement CU
       LEFT JOIN 
          inserted on CU.id = inserted.id
       LEFT JOIN 
          deleted on CU.id = deleted.id
       WHERE
          (inserted.id is not null or deleted.id is not null)
          AND IsNull(inserted.OpeningBalance,'') <> IsNull(deleted.OpeningBalance,'')
   ...

然而,这非常昂贵,尤其是当有很多字段时,任何人都可以提出一种更有效的方法来跟踪我们的变更日志的更改吗?

【问题讨论】:

    标签: sql sql-server database performance tsql


    【解决方案1】:

    我同意 Madison 的观点,AUDIT 表中包含整条记录的单行将更有用且性能更高。基表越宽,这一点就越真实。在你的情况下:

    CREATE TRIGGER IUD_Statement_Audit
    ON dbo.tbl_Statement AFTER INSERT, UPDATE, DELETE
    AS BEGIN
    
      IF (@@rowCount = 0) RETURN;
    
      SET NOCOUNT ON;
    
      INSERT INTO dbo.tbl_Statement_AUDIT
        (id, idAccount, OpeningBalance, insertedOrDeleted, modTime, modId)
      SELECT id, idAccount, OpeningBalance, 'D', GETDATE(), USER_NAME() FROM DELETED
      UNION ALL
      SELECT id, idAccount, OpeningBalance, 'I', GETDATE(), USER_NAME() FROM Inserted
    
    END
    

    这种方法有很多好处,包括:轻松加入 id 列以查看所有表的所有更改和触发器成为复制/粘贴练习。一般来说,RDBMS 在宽表(包含所有列的一条记录)和较少记录的情况下会表现得更好,而不是一个瘦 [?] 表(每列更改一条记录)。您当前的方法将为插入、删除和更新的每条记录创建三个记录。

    可以通过按modTime desc, insertedOrDeleted desc 对审计表进行排序并执行逆操作来回滚到过去的点...尽可能地回滚。

    【讨论】:

    • 经验教训...需要编写代码来获得支持! =)
    【解决方案2】:

    我有时会创建一个与我想要记录的表相同的表,但我添加了一个新的身份主键和一个日期时间字段来跟踪更改发生的时间。然后,每当原始表更改时,我将更改的整行加上当前日期时间插入到跟踪表中。

    这样做的优点是它很容易实现,并且可以在需要时回滚到以前的一组值。如果您想查看总历史记录,它还允许轻松加入当前行(如果您经常这样做,请记住索引)。此外,在您的情况下,它只是一个插入,除了抓取正确的行之外没有任何逻辑。缺点是每次发生任何变化时它都会存储所有字段,并且您必须维护两个表。

    您显然可以减少您不想跟踪的字段,或者以更符合您需求的其他方式修改这些字段。例如,如果行被删除,您可能希望在跟踪表中保留一个额外的字段。

    要查看旧值和新值,您只需按日期向后查看表格即可查看更改时间。

    【讨论】:

      【解决方案3】:

      我认为Log Trigger 中描述的方法更短、更快、更清晰。它不存储更改的“类型”,例如插入、更新或删除,而是创建一个名为Tuple versioning 的结构。插入是没有先前“版本”的行,删除是没有后续“版本”的行,中间行是更新。

      它的优点是在特定时间点检索特定实体的数据更容易、更自然。

      事实上,很容易了解整个表格在特定时间点的情况。如果表变得凌乱并且需要从给定的“安全”时间点恢复数据,这很有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-30
        相关资源
        最近更新 更多