我有很多表通过我的应用程序更新,这些表返回了大量数据或难以查询更改。为了解决这个问题,我创建了一个带有单行的“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中的数据并不重要,但将数据正确保存到复杂表中是
这可能是我忽略或不知道的非常简单的事情。一如既往,感谢任何信息。
答案 0 :(得分:3)
我想说你应该考虑使用Change Tracking
(http://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)
架构需要改变。如果必须保留更新表,请为每个表创建一行。这会大大减少你的锁,因为每个表都可以更新自己的行,而不是竞争表中的唯一行。
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
表每天都会增长,您需要处理这些表。