SQL触发器将UPDATE记录到Audit Table中

时间:2014-09-29 15:28:06

标签: sql sql-server-2008-r2

我的表格中列businessnamesortcodeaccountnumber已填充,namenationalityDOB所有目前都未填充。我需要创建一个触发器,以便在更新任何空字段时将每次更新都发送到审计表中,这样如果我只更改名称,我将获得时间戳,用户ID,字段更改,旧值和新value ..如果我更改了所有3个空字段,我想将3行发送到审计表。

有人可以给我一个关于这个逻辑的指针吗?

用于测试的非常基本的格式我已经

CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE
ON UPDATE 
AS 
    INSERT INTO RM_AUDITLOG
       SELECT CURRENT_TIMESTAMP, SRT_CD
       FROM RM_BASE

但这是在UPDATE之后将所有当前行发送到其中任何一行,我只想要受影响的行。我不确定是否应该构建更多的表来连接在一起以获得最终答案或使用INSERT / DELETE表。我以前的角色已经看过这种格式的审计表,所以我知道它有效,但无法弄清楚!

由于

1 个答案:

答案 0 :(得分:0)

是的,您需要使用INSERTED和/或DELETED伪表,因为它们只包含已修改的行。如:

CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE
ON UPDATE 
AS 
    INSERT INTO RM_AUDITLOG
       SELECT CURRENT_TIMESTAMP, SRT_CD
       FROM INSERTED

INSERTED表具有每行的“新”或“当前”版本,而DELETED表具有已通过UPDATE操作替换的“旧”版本。所有版本的SQL Server都是这种情况,至少可以追溯到SQL Server 2000。

为了跟踪更改本身(“旧”和“新”值),您需要加入这两个伪表,如:

  INSERT INTO RM_AUDITLOG
     SELECT CURRENT_TIMESTAMP, ins.SRT_CD AS [SRT_CD_new], del.SRT_CD AS [SRT_CD_old]
     FROM INSERTED ins
     INNER JOIN DELETED del
             ON del.PKfield = ins.PKfield

这是在DML触发器中捕获更改的基本操作(当然,除非您使用Change Data Capture)。

如果您希望unpivot这些数据使得每组“旧”和“新”列成为一行,那么应该可以很容易地从上面进行调整。在这种情况下,您还可以添加WHERE ISNULL(ins.column, '~~~~') <> ISNULL(del.column, '~~~~') COLLATE Latin1_General_BIN以避免捕获未更改的字段。 COLLATE可确保区分大小写/重音敏感/等比较。

当然,由于您需要永久保留完整的历史记录,因此对于整个行的重建非常困难。您需要从所有字段的基值开始并逐步应用更改。典型的审计方案是捕获每个源字段都有旧字段和新字段的行(就像我已经显示的那样)。如果您的审计表看起来更像:

PKfield, DateModified, businessname_old, businessname_new, sortcode_old, sortcode_new

然后你可以编写一个查询来通过比较每个集合来识别实际更改的字段(假设在同一个UPDATE操作中可以更改多个字段),例如:

SELECT PKfield,
       DateModified,
       CASE
         WHEN ISNULL(businessname_old, '~~~~') <> ISNULL(businessname_new, '~~~~')
              COLLATE Latin1_General_BIN THEN 'BusinessName ' ELSE ''
       END + 
       CASE
         WHEN ISNULL(sortcode_old, '~~~~') <> ISNULL(sortcode_new, '~~~~')
              COLLATE Latin1_General_BIN THEN 'SortCode ' ELSE ''
       END AS [FieldsChanged]
FROM  AuditTable
ORDER BY DateModified DESC;

但是,如果确实想要将数据展开以使每个实际更改的字段有一行,那么以下结构应该有效:

;WITH ins AS
(
   SELECT PKfield, FieldName, Value
   FROM   (
            SELECT PKfield, businessname, sortcode, accountnumber, name,
                   nationality, DOB
            FROM INSERTED
          ) cols
   UNPIVOT (Value FOR FieldName IN
               (businessname, sortcode, accountnumber, name, nationality, DOB)
           ) colvals
), del AS
(
   SELECT PKfield, FieldName, Value
   FROM   (
            SELECT PKfield, businessname, sortcode, accountnumber, name,
                   nationality, DOB
            FROM DELETED
          ) cols
   UNPIVOT (Value FOR FieldName IN
               (businessname, sortcode, accountnumber, name, nationality, DOB)
           ) colvals
)
INSERT INTO AuditTable (PKfield, DateModified, FieldName, OldValue, NewValue)
   SELECT ins.PKfield, CURRENT_TIMESTAMP, ins.FieldName, del.Value, ins.Value
   FROM   ins
   INNER JOIN del
           ON del.PKfield = ins.PKfield
          AND del.FieldName = ins.FieldName
   WHERE  ISNULL(del.Value, '~~~~') <>
             ISNULL(ins.Value, '~~~~') COLLATE Latin1_General_BIN;

如果CONVERT(VARCHAR(1000), field)WHEREDOB字段,可能需要向DATE条件添加DATETIME,或者如果SRT_CD是INT或其他类型的数字字段:

   WHERE  ISNULL(CONVERT(VARCHAR(1000), del.Value), '~~~~') <>
             ISNULL(CONVERT(VARCHAR(1000), ins.Value), '~~~~')
             COLLATE Latin1_General_BIN;