基于触发器的历史记录

发布于 2024-07-17 04:49:58 字数 1235 浏览 8 评论 0原文

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

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 =(从已删除中选择“列名”);

What I am trying to do is find out which fields were updated and record the change to a different table.

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

what is happening is the

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

are setting the @OldValue and @NewValue = to the columnname instead of the value of the column – sql is processing it as SET @OldValue = (SELECT 'column_name' FROM Deleted);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

在你怀里撒娇 2024-07-24 04:49:58

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

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

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

What I am trying to do is find out which fields were updated

In SQL Server there are two functions that does exactly what you are looking for.

  • Columns_Updated() - Check if one or more column(s) is/are inserted/deleted within trigger
  • Update() - Check if a single column is updated within trigger
2024-07-24 04:49:58

请参阅此 弹出审计跟踪 它使用循环中的查询而不是游标来执行您想要执行的操作。

See this Pop on the Audit Trail It uses a query in a loop as opposed to a cursor, to do just what you're wanting to do.

枉心 2024-07-24 04:49:58

这不起作用:

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

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

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

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

This wont work:

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

You're attempting dynamic sql here, which won't work. You have to hard-code the SQL, the variable @column_name will not be dynamically replaced with its value, because the SQL of the trigger gets parsed once, before the trigger runs. With this, you'll (depending on your settings) probably get the literal value of the column name.

It is possible to get dynamic SQL (by connecting to the server in another process, or in MySQl by creating a prepared statement), but it's not possible to do that and reference the "magic" INSERTED and DELETED pseudo-tables available in a trigger.

So your clever use of information_schema.columns won't work. What you can do is leverage that cleverness to write a stored procedure to generate the trigger (this is in fact what I did when I had to write auditing triggers). Then whenever you change the Shipment table, you'll have to run the sp to generate the "create trigger...." statmentnt, and then run that generated statement to re-create the trigger.

雨落星ぅ辰 2024-07-24 04:49:58

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

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

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

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

I'd rethink your whole process. Triggers can be huge performance killers when written improperly. Anytime you think you need to use a cursor or a loop, think again. You need to do this in a set-based fashion.

We use a two table trigger approach. One that records the details about when and who changed the table and a related table that contains the information that was changed. This helps us see all records that were changed at one time. We use an updated statement for each field to populate the second table something like:

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

We rebuild the trigger code dynamically every time the schema is changed, but the trigger itself is not dynamic. Our trigger process runs very fast even when we do large imports.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文