Sql 在触发器中记录更新操作

Sql 在触发器中记录更新操作,sql,triggers,sql-server-2012,Sql,Triggers,Sql Server 2012,我有一个更新触发器,它生成插入和删除的表,如下所示: 插入 删除 我想将此更新捕获到日志表中。所有表的全局日志表如下所示,然后我必须处理插入和删除的表: Id_Table Table_Key_Value Id_Value Old_Value New_Value 12345 1 4556645 Stack Stacks 12345 1 544589 Overflow

我有一个更新触发器,它生成插入和删除的表,如下所示:

插入

删除

我想将此更新捕获到日志表中。所有表的全局日志表如下所示,然后我必须处理插入和删除的表:

Id_Table    Table_Key_Value   Id_Value   Old_Value  New_Value
12345               1          4556645    Stack      Stacks
12345               1           544589   Overflow   Overflows
12345               2           544589   Saggese       Sag
Id_Table是表的系统对象,我在其中执行了UPDATE语句,Table_Key_Value是更新列的主键的值,Id_Value是映射到每个表中每个列的自定义Id。仅当更新更改了列时,才会记录列的数据

我想到了两种方法:

对表执行选择,每列一次:

INSERT INTO LOG (Id_Table, Table_Key_Value, Id_Value,Old_Value, New_Value)
   SELECT 12345, Id, 4556645, D.Name, I.Name
   FROM INSERTED I 
   INNER JOIN DELETED D ON I.ID = D.ID
   WHERE D.Name <> I.Name

   union

   SELECT 12345, Id, 544589, D.Surname, I.Surname
   FROM INSERTED I 
   INNER JOIN DELETED D ON I.ID = D.ID
   WHERE D.Surname <> I.Surname

还有其他方法吗?什么是最有效和可维护的方法?

在回答之前,让我先说一句,我不认为最好将所有表记录到一个表中。如果数据库增长,可能会导致日志表上的严重争用。另外,您的所有数据都必须更改为varchar或sql_variant才能放在同一列中,这迫使它占用更多空间。我还认为,将每个更新的列记录到一个单独的行中,跳过未更新的列将使您很难进行查询。您知道如何将所有这些数据汇集在一起,以实际获得每一行的更改、更改时间和更改人的综合和合理视图吗?在我看来,每个表有一个日志表会容易得多。这样,您就不会遇到您在尝试使其工作时遇到的问题

另外,您知道SQL Server 2008吗?如果您使用的是SQL Server的企业版或开发人员版,请改用它

除此之外,您还可以使用逻辑UNPIVOT执行您自己的版本。您不能真正使用本机SQL 2005 UNPIVOT,因为您有两个目标列,而不是一个。下面是SQL Server 2005及更高版本使用交叉应用执行取消PIVOT的示例:

INSERT INTO dbo.LOG (Id_Table, Table_Key_Value, Id_Value, Old_Value, New_Value)
SELECT 12345, I.Id, X.Id_Value, X.Old_Value, X.New_Value
FROM
   INSERTED I 
   INNER JOIN DELETED D ON I.ID = D.ID
   CROSS APPLY (
      SELECT 4556645, D.Name, I.Name
      UNION ALL SELECT 544589, D.Surname, I.Surname
    ) X (Id_Value, Old_Value, New_Value)
WHERE
   X.Old_Value <> X.New_Value
SQLServer2005和更高版本确实有本机UNPIVOT命令,但一般来说,即使UNPIVOT可以工作,我也喜欢使用交叉应用,因为这样做更灵活。具体来说,本机UNPIVOT命令在这里不可用,因为UNPIVOT只能针对单个目标列,但需要两个旧的\u值和新的\u值。将两列连接成一个值,然后再分离是不好的;创建一个无意义的行相关器值以供以后使用是不好的,我想不出另外一种方法来实现它,这不是这两种方法的变体。交叉应用解决方案确实将是最适合您匹配您所描述的确切日志表结构的解决方案


与我在这里的查询相比,您的方法1在{the number of columns}:1的比率下的性能不会很好:性能更差。您的方法2是一个好主意,但仍然不太理想,因为调用UDF有很大的开销,而且您必须在每一行上进行循环。

您正在使用的任何特定DBMS?SQL Server、Oracle、MySQL、PostGreSql、SQLite?对于您的第一个问题,我使用了一个表来映射具有我自己ID的所有列和表。这样,如果我要更改数据库,我就可以创建表或其他。。。。对于你的第二个问题,我有一个单表日志,这个独特的表日志供我内部使用,它不是太多…我没有它的视图,只有一个分析它的Windows服务。那么,如何在MS SQL Server 2012上使用UNPIVOT命令?这个命令perform?我在这里向您展示的方法将为UNPIVOT提供同等或更好的性能。另外,UNPIVOT只适用于一个目标列,因此您必须在只需要部分UNPIVOT之后进行透视。至少在17个小时内,我没有时间给你一个PIVOT版本。
INSERT INTO LOG (Id_Table, Table_Key_Value, Id_Value,Old_Value, New_Value)
   SELECT 12345, Id, 4556645, D.Name, I.Name
   FROM INSERTED I 
   INNER JOIN DELETED D ON I.ID = D.ID
   WHERE D.Name <> I.Name

   union

   SELECT 12345, Id, 544589, D.Surname, I.Surname
   FROM INSERTED I 
   INNER JOIN DELETED D ON I.ID = D.ID
   WHERE D.Surname <> I.Surname
SELECT CustomFunction(12345,Id, I.Name, D.Name, I.Surname, D.Surname) 
FROM INSERTED I  
INNER JOIN DELETED D ON I.ID = D.ID

**CustomFunction** (_Id_Table,_Table_Key_Value, _Old_Value_Name, _New_Value_Name, _Old_Value_Surname, _New_Value_Surname)

INSERT INTO LOG(Id_Table, Table_Key_Value, Id_Value,Old_Value, New_Value)
VALUES(_Id_Table,_Table_Key_Value, 4556645, _Old_Value_Name, _New_Value_Name)

INSERT INTO LOG(Id_Table, Table_Key_Value, Id_Value,Old_Value, New_Value)
VALUES(_Id_Table,_Table_Key_Value, 544589, _Old_Value_Surname, _New_Value_Surname)
INSERT INTO dbo.LOG (Id_Table, Table_Key_Value, Id_Value, Old_Value, New_Value)
SELECT 12345, I.Id, X.Id_Value, X.Old_Value, X.New_Value
FROM
   INSERTED I 
   INNER JOIN DELETED D ON I.ID = D.ID
   CROSS APPLY (
      SELECT 4556645, D.Name, I.Name
      UNION ALL SELECT 544589, D.Surname, I.Surname
    ) X (Id_Value, Old_Value, New_Value)
WHERE
   X.Old_Value <> X.New_Value
INSERT INTO dbo.LOG (Id_Table, Table_Key_Value, Id_Value, Old_Value, New_Value)
SELECT *
FROM (
   SELECT
      12345,
      I.Id,
      X.Id_Value,
      CASE X.Id_Value
         WHEN 4556645 THEN D.Name
         WHEN 544589 THEN D.Surname
      END Old_Value,
      CASE X.Id_Value
         WHEN 4556645 THEN I.Name
         WHEN 544589 THEN I.Surname
      END New_Value   
   FROM
      INSERTED I 
      INNER JOIN DELETED D ON I.ID = D.ID
      CROSS JOIN (
         SELECT 4556645
         UNION ALL SELECT 544589
      ) X (Id_Value)
) Y
WHERE
   Y.Old_Value <> Y.New_Value