基于触发器的历史记录

时间:2009-04-13 22:47:26

标签: sql triggers history tracking sql-update

我要做的是找出哪些字段已更新,并将更改记录到另一个表中。

DECLARE 
    @BillNo int,
    @column_name varchar(500)  

SELECT @BillNo = BillNo FROM INSERTED
DECLARE HistoryMonitorLoop CURSOR FOR
    SELECT    
        column_name 
    FROM 
        information_schema.columns
    WHERE 
        table_name = 'Shipment';
OPEN HistoryMonitorLoop
FETCH next FROM HistoryMonitorLoop INTO @column_name
WHILE @@Fetch_status = 0
BEGIN
    DECLARE
        @OldValue varchar(500),
        @NewValue varchar(500)
    SET @OldValue = (SELECT @column_name FROM Deleted);
    SET @NewValue = (SELECT @column_name FROM Inserted);
    IF(@OldValue != @NewValue)
    BEGIN
        DECLARE @Comment varchar(5000)
        SELECT @Comment = @column_name + ' Changed from ' + @OldValue + ' to ' + @NewValue
        EXEC ShipmentNote_Insert @BillNo=@BillNo,@CoordinatorID=1,@Comment=@Comment
    END
    FETCH next FROM HistoryMonitorLoop INTO @column_name
END
CLOSE HistoryMonitorLoop
DEALLOCATE HistoryMonitorLoop

正在发生的事情是

SET @OldValue = (SELECT @column_name FROM Deleted);   
SET @NewValue = (SELECT @column_name FROM Inserted); 

@OldValue@NewValue =设置为列名而不是列的值 - sql将其处理为SET @OldValue = (SELECT 'column_name' FROM Deleted);

4 个答案:

答案 0 :(得分:3)

  

我要做的是找出哪些字段已更新

在SQL Server中,有两个功能可以完全满足您的要求。

  • Columns_Updated() - 检查是否在触发器中插入/删除了一个或多个列
  • Update() - 检查触发器
  • 中是否更新了单个列

答案 1 :(得分:1)

请参阅此Pop on the Audit Trail它在循环中使用查询而不是光标,以执行您想要执行的操作。

答案 2 :(得分:0)

这不起作用:

SET @OldValue = (SELECT @column_name FROM Deleted);
SET @NewValue = (SELECT @column_name FROM Inserted);

你在这里尝试动态sql,这是行不通的。您必须对SQL进行硬编码,变量@column_name将不会被其值动态替换,因为触发器的SQL在触发器运行之前会被解析一次。有了这个,你(根据你的设置)可能会得到列名的字面值。

可以获得动态SQL(通过在另一个进程中连接到服务器,或通过创建预准备语句在MySQl中连接),但是不可能引用触发器中可用的“magic”INSERTEDDELETED伪表。

因此,您巧妙地使用information_schema.columns将无法正常工作。你能做的就是利用这种聪明才能编写存储过程来生成触发器(这实际上就是我在编写审计触发器时所做的事情)。然后每当你更改Shipment表时,你都必须运行sp来生成“create trigger ....”statmentnt,然后运行那个生成的语句来重新创建触发器。

答案 3 :(得分:0)

我会重新考虑你的整个过程。如果写得不正确,触发器可能是巨大的性能杀手。只要您认为需要使用游标或循环,请再想一想。您需要以基于集合的方式执行此操作。

我们使用两表触发器方法。记录有关何时更改表以及更改表的人员以及包含已更改信息的相关表的详细信息。这有助于我们查看一次更改的所有记录。我们对每个字段使用更新的语句来填充第二个表,如:

if (update([test]))
  begin
    insert [myAudit].dbo.[mytableAuditLogDetail](AuditLogID, ID, ColumnName,   
                                                 OldValue, NewValue)
    select
      @AuditLogID,
      i.[mytableid]),
      'test',
      convert(varchar(8000), d.[test], 0),
      convert(varchar(8000), i.[test], 0)
    from  inserted i
    inner join deleted d on i.[mytableid]=d.[mytableid]
      and (
      (i.[test] <> d.[test]) or 
      (i.[test] is null and d.[test] Is Not Null) or
      (i.[test] is not null and d.[test] Is Null)
          )         
   end

每次更改架构时,我们都会动态重建触发器代码,但触发器本身不是动态的。即使我们进行大量进口,我们的触发过程也会非常快速。