基于触发器的历史记录
我想做的是找出哪些字段已更新并将更改记录到不同的表中。
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在 SQL Server 中,有两个函数可以完全满足您的需求。
In SQL Server there are two functions that does exactly what you are looking for.
请参阅此 弹出审计跟踪 它使用循环中的查询而不是游标来执行您想要执行的操作。
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.
这不起作用:
您在这里尝试动态 sql,这不起作用。 您必须对 SQL 进行硬编码,变量
@column_name
不会动态替换为其值,因为触发器的 SQL 在触发器运行之前会被解析一次。 这样,您(根据您的设置)可能会获得列名称的字面值。可以获取动态 SQL(通过在另一个进程中连接到服务器,或者在 MySQl 中通过创建准备好的语句),但不可能做到这一点和参考触发器中可用的“神奇”
INSERTED
和DELETED
伪表。因此,您对 information_schema.columns 的巧妙使用将不起作用。 您可以做的是利用这种聪明才智编写一个存储过程来生成触发器(这实际上是我在编写审计触发器时所做的)。 然后,每当您更改
Shipment
表时,您都必须运行 sp 来生成“创建触发器...”statmentnt,然后运行该生成的语句来重新创建触发器。This wont work:
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
andDELETED
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.我会重新考虑你的整个过程。 如果编写不当,触发器可能会成为巨大的性能杀手。 每当您认为需要使用游标或循环时,请再考虑一下。 您需要以基于集合的方式执行此操作。
我们使用两个表触发器方法。 记录有关何时以及谁更改表的详细信息的表以及包含已更改信息的相关表。 这有助于我们查看一次性更改的所有记录。 我们对每个字段使用更新的语句来填充第二个表,如下所示:
每次架构更改时,我们都会动态重建触发器代码,但触发器本身不是动态的。 即使我们进行大量导入,我们的触发流程也运行得非常快。
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:
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.