【问题标题】:Trigger based history基于触发器的历史
【发布时间】:2010-10-19 05:48:55
【问题描述】:

我要做的是找出更新了哪些字段并将更改记录到不同的表中。

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);

【问题讨论】:

  • 那么,脚本有效吗?你有错误吗?您只是在测试之前询问它是否还可以吗?
  • 发生了什么是 SET @OldValue = (SELECT @column_name FROM Deleted); SET @NewValue = (SELECT @column_name FROM Inserted);正在将 @OldValue 和 @NewValue = 设置为列名而不是列的值
  • sql 将其处理为 SET @OldValue = (SELECT 'column_name' FROM Deleted);

标签: sql triggers history tracking sql-update


【解决方案1】:

我会重新考虑你的整个过程。如果编写不当,触发器可能会成为巨大的性能杀手。每当您认为需要使用游标或循环时,请三思。您需要以基于集合的方式执行此操作。

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

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 做到了,现在我要做的就是弄清楚如何;)
    【解决方案3】:

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

    在 SQL Server 中,有两个函数可以完全满足您的需求。

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

    【讨论】:

      【解决方案4】:

      这行不通:

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

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

      可以获取动态 SQL(通过在另一个进程中连接到服务器,或在 MySQl 中通过创建准备好的语句),但不可能做到这一点 em> 引用触发器中可用的“魔术”INSERTEDDELETED 伪表。

      所以你对 information_schema.columns 的巧妙使用是行不通的。你可以做的是利用这种聪明来编写存储过程来生成触发器(这实际上是我在编写审计触发器时所做的)。然后,每当您更改 Shipment 表时,您都必须运行 sp 以生成“create trigger....”statmentnt,然后运行 ​​that 生成的语句以重新创建触发器.

      【讨论】:

      • ShipmentNote_Insert 添加了日期戳,因此这不是问题。就性能而言,发货表中的数据本身并不经常更新,但当它更新时,我们需要知道发生了什么变化,第三方应用程序会使用这些通知。表格的结构现在非常流畅
      • 好的,那你有什么问题?
      猜你喜欢
      • 2012-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-01
      • 1970-01-01
      • 2016-07-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多