sql-servertimestamprowversion

Need help understand this example about SQL Server rowversion?


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.


Solution

  • 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));