将更新记录到审核表中的SQL触发器

将更新记录到审核表中的SQL触发器,sql,sql-server-2008-r2,Sql,Sql Server 2008 R2,我有一个表,其中列businessname、sortcode和accountnumber都已填充,名称、国籍和DOB都当前未填充。我需要创建一个触发器,在更新任何空字段时触发对审计表的每次更新,因此如果我只更改名称,我将获得时间戳、用户ID、更改的字段、旧值和新值。。如果我更改了所有3个空字段,我将向审核表发送3行 有人能给我一个关于这个逻辑的指针吗 以一种非常基本的形式进行测试 CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE ON UPDATE AS

我有一个表,其中列businessname、sortcode和accountnumber都已填充,名称、国籍和DOB都当前未填充。我需要创建一个触发器,在更新任何空字段时触发对审计表的每次更新,因此如果我只更改名称,我将获得时间戳、用户ID、更改的字段、旧值和新值。。如果我更改了所有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
但这是在更新任何一行之后发送所有当前行,我只想要受影响的行。我不确定是应该构建更多的表来连接在一起以获得最终答案,还是使用INSERT/DELETE表。。我在以前的角色中看到过这种格式的审计表,所以我知道它可以工作,但无法理解

谢谢

是的,您需要使用插入和/或删除的伪表,因为这些伪表只包含已修改的行。例如:

CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE
ON UPDATE 
AS 
    INSERT INTO RM_AUDITLOG
       SELECT CURRENT_TIMESTAMP, SRT_CD
       FROM INSERTED
插入的表具有每行的新版本或当前版本,而删除的表具有已通过更新操作替换的旧版本。所有版本的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触发器中使用

如果您希望将这些数据转换为一行,使每组旧列和新列成为一行,那么应该可以很容易地从上面进行调整。在这种情况下,您还可以添加WHERE ISNULLins.column、~~~~'ISNULLdel.column、~~~~'COLLATE Latin1\u General\u BIN,以避免捕获未更改的字段。COLLATE确保区分大小写/区分重音/etc比较

当然,取消激活会使重建整行变得非常困难,因为您需要永远保存完整的历史记录。您需要从所有字段的基值开始,并以增量方式应用更改。典型的审核场景是只捕获一行,该行的每个源字段都有新旧字段,如我已经展示的。如果您的审核表看起来更像:

PKfield,DateModified,businessname_old,businessname_new,sortcode_old,sortcode_new

然后,您可以编写一个查询,通过比较每个集合来确定实际更改的字段,因为在相同的更新操作中可以更改多个字段,例如:

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;

抱歉-SQLServer2008R2AH太棒了,谢谢-我从未使用过伪表。如何获取已从插入或删除中更新的字段名?@user3294008,首先,我只是重新阅读了这个问题,听起来您只想在字段从NULL变为具有值时捕获字段,而不是在非NULL值变为另一个非NULL值时捕获字段。这是正确的还是问题中的措辞错误?不-我想捕获从现在开始的所有更改,无论是空的还是填充的,只是它们都是当前的null@user3294008,这更有意义,这也是我最初的设想。唯一不完全是您想要的部分是需要首先取消伪表。我可以将我的答案更新为基本结构,但您必须从那里完成它。这很好-我理解所有的unpivot,老实说,这不是我的解决方案的要求,但我真的想引入已更新的列名-有没有快速简单的方法来做到这一点?
;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;
   WHERE  ISNULL(CONVERT(VARCHAR(1000), del.Value), '~~~~') <>
             ISNULL(CONVERT(VARCHAR(1000), ins.Value), '~~~~')
             COLLATE Latin1_General_BIN;