在更新语句中获取最新的rowversion/timestamp值 - Sql Server

6

我正在使用rowversion列来处理乐观并发,并希望在更新完成后获取新的rowversion值,以便我的数据层具有最新值,可以执行另一个更新操作,而不会出现并发异常(除非记录已被其他人更新)。

之前我在更新后在数据层进行了一次获取,但这不够高效或完全可靠。

对于下面的表:

CREATE TABLE PurchaseType
(
    PurchaseTypeCode nvarchar(20) NOT NULL PRIMARY KEY CLUSTERED (PurchaseTypeCode),
    Name nvarchar(50) NOT NULL,
    TS rowversion NOT NULL
)

我尝试过:

CREATE PROCEDURE PurchaseType_UpdateWithGet
@PurchaseTypeCode nvarchar(20),
@Name nvarchar(50),
@TS rowversion OUTPUT    
AS

UPDATE PurchaseType
SET Name = @Name
WHERE PurchaseTypeCode = @PurchaseTypeCode
 AND TS = @TS

SELECT @TS = TS FROM PurchaseType WHERE PurchaseTypeCode = @PurchaseTypeCode
GO

但我并不完全满意,因为存在无法从别人的更新中获取rowversion值的可能性。然后我在rowversion文档(http://msdn.microsoft.com/en-us/library/ms182776.aspx / http://msdn.microsoft.com/en-us/library/ms177564.aspx)中发现了OUTPUT语句,并尝试了以下操作:

CREATE PROCEDURE PurchaseType_UpdateWithOutput
@PurchaseTypeCode nvarchar(20),
@Name nvarchar(50),
@TS rowversion OUTPUT    
AS

DECLARE @Output TABLE (TS BINARY(8))

UPDATE PurchaseType
SET Name = @Name
    OUTPUT inserted.TS into @Output
WHERE PurchaseTypeCode = @PurchaseTypeCode
    AND TS = @TS

SELECT TOP 1 @TS = TS FROM @Output
GO

这个方法很有效。在我的基础测试中(只是运行了10000次调用并计时),使用OUTPUT选项需要的时间约长40%,但仍然少于半毫秒。在SET STATISTICS TIME ON下,两者都没有花费可测量的时间。

我的问题是,有没有人知道更好/更简单的方法来做到这一点?我希望能够使用类似于标识列的SCOPE_IDENTITY()函数的函数,但找不到类似的东西。有人知道我是否遗漏了什么吗?

提前感谢。


输出是检索INSERT生成的任何值的最佳方法(如果您问我,包括IDENTITY ...) - Remus Rusanu
好的,谢谢。您偏爱 OUTPUT 而不是 SCOPE_IDENTITY 的特别原因是什么? - Simon Vane
3
它可以处理多行插入。 - Remus Rusanu
请注意,如果将触发器添加到 PurchaseType 表中,OUTPUT 将会失败。详见:https://dev59.com/qWYs5IYBdhLWcg3wBvb7。 - Ian Boyd
2个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
1

来自MSDN

@@DBTS返回当前数据库的最后使用时间戳值。 当插入或更新具有时间戳列的行时,会生成新的时间戳值。


2
谢谢,我不知道这个。但除非我弄错了,它在这种情况下不会可靠工作,因为它是数据库范围内的(类似于@@identity),而不是过程范围内的(类似于SCOPE_IDENTITY())。 - Simon Vane
您是对的,那确实是一个缺点。我猜 OUTPUT 是唯一剩下的选择了。 - Guillermo Gutiérrez

0
你的问题在于在更新和选择之间,有其他人更新了行。因此,在执行更新语句后进行选择时,您将无法获得相同的值。这是不可重复读取的问题。您应该在更新语句中使用锁提示(UPDLOCK)。此锁定将允许读取器(即共享锁定),但将阻止编写器。因此,在更新后,您将获得相同的行。因此,您的存储过程应该是 -
CREATE PROCEDURE PurchaseType_UpdateWithGet
@PurchaseTypeCode nvarchar(20),
@Name nvarchar(50),
@TS rowversion OUTPUT    
AS

UPDATE PurchaseType with (updlock)
SET Name = @Name
WHERE PurchaseTypeCode = @PurchaseTypeCode
 AND TS = @TS

SELECT @TS = TS FROM PurchaseType WHERE PurchaseTypeCode = @PurchaseTypeCode
GO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,