Before reading this example, I can understand rowversion myself that it reflects the last updated timestamp on a record. I think about its usage like this: First after reading a record, the rowversion column value should be achieved. Then before updating that record, the locally stored rowversion value should be checked against the current rowversion value fetched from database (at the time before updating), if they are not equal then it means there has been some update from another user and the current app should handle that concurrency situation with its own strategy.
However I think the following example either over-complicates the problem or may be even wrong or poorly explained (so lead to confusion):
CREATE TABLE MyTest (myKey int PRIMARY KEY
,myValue int, RV rowversion);
GO
INSERT INTO MyTest (myKey, myValue) VALUES (1, 0);
GO
INSERT INTO MyTest (myKey, myValue) VALUES (2, 0);
GO
DECLARE @t TABLE (myKey int);
UPDATE MyTest
SET myValue = 2 OUTPUT inserted.myKey INTO @t(myKey)
WHERE myKey = 1 AND RV = myValue;
IF (SELECT COUNT(*) FROM @t) = 0
BEGIN
RAISERROR ('error changing row with myKey = %d'
,16 -- Severity.
,1 -- State
,1) -- myKey that was changed
END;
I notice myValue
here, it's set to 2
and also used in the WHERE clause to check against the RV
column. As my understand the rowversion column is obviously RV
but then it explains this:
myValue is the rowversion column value for the row that indicates the last time that you read the row. This value must be replaced by the actual rowversion value
I didn't think myValue
has anything to do with rowversion here, it should just be considered as user data. So with such explanation, the MyTest
table has 2 rowversion columns? while myValue
is obviously declared as int
?
A possibility I can think of is myValue
in WHERE condition is understood differently (meaning it was not the myValue
in the SET clause), it may be just a placeholder such as for the read value of RV
at the time reading the record before. Only that possibility makes sense to me.
So as I understand the example should be like this:
SET myValue = 2 OUTPUT inserted.myKey INTO @t(myKey)
WHERE myKey = 1 AND RV = rowVersionValueFromTheLastTimeReading
I've heard of timestamp
before but rowversion
is fairly new to me and once I tried finding more about it, I found this example making me so confused. What is your idea about this? Or I simply don't understand some of the mysterious usages of rowversion? Thanks.
The example in the Books Online is incorrect. I see that was called out in the community comments for the topic.
The code below shows how one might use rowversion
to implement optimistic concurrency. This method is often employed when data are presented to the user for update and then modified.
DECLARE
@MyKey int = 1
,@NewMyValue int = 1
,@OriginalMyValue int
,@OriginalRV rowversion
--get original data, including rowversion
SELECT
@OriginalMyValue = myValue
, @OriginalRV = RV
FROM dbo.MyTest
WHERE myKey = 1;
--check original rowversion value when updating row
UPDATE dbo.MyTest
SET myValue = @NewMyValue
WHERE
myKey = 1
AND RV = @OriginalRV;
--optimistic concurrency violation
IF @@ROWCOUNT = 0
RAISEERROR ('Data was updated or deleted by another user.', 16, 1);
Alternatively, the original data value(s) can be checked instead of rowversion
. However, this gets unwieldy if you have a lot of columns and need to check for NULL
values. That's where rowversion
is handy.
--check original rowversion value when updating row
UPDATE dbo.MyTest
SET myValue = @NewMyValue
WHERE
myKey = 1
AND (myValue = @OriginalMyValue
OR (myValue IS NULL AND @OriginalMyValue IS NULL));