审计表上的触发因更新冲突而失败

时间:2012-11-01 01:26:07

标签: sql sql-server

我有很多表通过我的应用程序更新,这些表返回了大量数据或难以查询更改。为了解决这个问题,我创建了一个带有单行的“LastUpdated”表,并在这些复杂的表上设置了一个触发器,它只是将GetDate()设置为LastUpdated表中的相应列:

CREATE TRIGGER [dbo].[trg_ListItem_LastUpdated] ON [dbo].[tblListItem] 
FOR INSERT, UPDATE, DELETE 
AS
UPDATE LastUpdated SET ListItems = GetDate()
GO

这样,客户端只需要查询该表以获取最后更新的值,然后就可以决定是否需要从复杂表中刷新数据。复杂表使用快照隔离来防止脏读。

在繁忙的系统中,由于“LastUpdated”中的更新冲突,我们每天大约一次在复杂表中编写或更新数据时出错。因为这发生在触发器执行的语句中,受影响的复杂表无法保存数据。记录以下错误:

  

由于更新冲突导致快照隔离事务中止。您   无法使用快照隔离来访问表'dbo.tblLastUpdated'   直接或间接在数据库'devDB'中更新,删除或   插入已被其他人修改或删除的行   交易。重试事务或更改隔离级别   更新/删除声明。

我应该在触发器中做什么来防止这种失败?我可以在触发器上使用某种查询提示来避免这种情况 - 或者我可以忽略触发器中的错误吗?更新LastUpdated中的数据并不重要,但将数据正确保存到复杂表中是

这可能是我忽略或不知道的非常简单的事情。一如既往,感谢任何信息。

4 个答案:

答案 0 :(得分:3)

我想说你应该考虑使用Change Trackinghttp://msdn.microsoft.com/en-gb/library/cc280462%28v=sql.100%29.aspx),这是一个轻量级的内置SQL Server功能,你可以使用它监视一个表已经改变的事实,而不是记录每个单独的更改(您也可以使用Change Data Capture)。它需要您正在使用的快照隔离。

由于您的触发器正在父事务中运行,并且您的快照已过期,因此您需要重新启动整个事务。如果这是一个复杂的工作负载,以这种方式维护这最后更新的数据将是代价高昂的。

答案 1 :(得分:1)

简短回答 - 不要那样做!使更新的事务依赖于单个共享行使得它容易出现死锁,并且更新了整个令人讨厌的事情。

您可以使用视图来确定上次更新,例如:

SELECT
    t.name
    ,user_seeks
    ,user_scans
    ,user_lookups
    ,user_updates
    ,last_user_seek
    ,last_user_scan
    ,last_user_lookup
    ,last_user_update
FROM sys.dm_db_index_usage_stats i JOIN sys.tables t 
    ON (t.object_id = i.object_id)
WHERE database_id = db_id()

或者,如果您确实坚持使用 LastUpdate 解决方案,则可以在自治事务中通过触发器实施更新。即使SQL Server不支持自治事务,也可以使用喜欢的服务器完成:How to create an autonomous transaction in SQL Server 2008

答案 2 :(得分:1)

架构需要改变。如果必须保留更新表,请为每个表创建一行。这会大大减少你的锁,因为每个表都可以更新自己的行,而不是竞争表中的唯一行。

LASTUPDATED

table_name (varchar(whatever)) pk
modified_date (datetime)

tblListItem

的新触发器
CREATE TRIGGER [dbo].[trg_ListItem_LastUpdated] ON [dbo].[tblListItem] 
FOR INSERT, UPDATE, DELETE 
AS
UPDATE LastUpdated SET modified_date = GetDate() WHERE table_name = 'tblListItem'
GO

我经常使用的另一个选项是在每个表中都有一个modified_date列。然后人们确切地知道要更新/插入哪些记录以与数据同步,而不是每次更改或插入一条记录时删除和重新加载表中的所有内容。

答案 3 :(得分:0)

或者,您可以在同一事务中更新日志表,该事务用于更新应用程序内的复杂表格。完全避免触发。

<强>更新 您也可以选择插入新行,而不是更新LastUpdated表中的同一行。然后,您可以查询最大时间戳以获取最新更新。但是,使用这种方法,如果交易量很高,您的LastUpdated表每天都会增长,您需要处理这些表。