简体   繁体   中英

Upsert with concurrency checking SQL Server

I have a system in production that I need to retrofit with concurrency checking due to increased use and some issues that have arisen.

Currently I'm using stored procedures that follow a basic upsert pattern:

Update X where key = @key 
if @@rowcount = 0
Insert into... 

Into that mix I need to check for row changes. I plan on using a Rowversion/Timestamp.

I don't want to just add:

Update ...  Where key = @key AND Row_Version = @Row_Version 

I want to fire a SQL error or something that can be caught by C# so that I can communicate the concurrency issue to the user, rather than just do nothing or a more "generic" error.

So, I think I'm going to have to ditch my Upsert and do things as follows:

-- Update Check
if exists (select @Current_Row_Version from table where key = @key)
    if @Current_Row_Version != @Row_Version 

        RAISEERROR('Concurrency Violation - TableName', 11,1);            
        Return
    else
        Update * set...  Where Key = @key

else
    -- Insert
    insert ... 

So, does that look like I'm the right track? Are there better patterns or practices?

Found some interesting examples such as here but nothing that quite fits my need. I could try the insert first as here but I think I'm going to be doing more updating than inserting.

Merge will not work for me either.

This Update/Insert will be part of a transaction as well... if any part fails, I need to roll everything back. The rollback is handled in C# code based on errors caught.

To ensure the that the row doesn't change between your if and your update , you could specify the locking hints updlock and holdlock :

begin transaction;
if exists (select @Current_Row_Version from table with (updlock, holdlock) where key=@key)
    begin
    if @Current_Row_Version != @Row_Version 
        begin
        rollback transaction;
        raiserror ('Concurrency Violation - TableName', 11,1);            
        return
        end
    else
        update * set...  Where Key = @key
    end
commit transaction;

Note that raiserror is written with one E .

A more common way to do optimistic locking is :

update   ...
set      ...
where    row_version = @target_row_version

And then check the number of updated rows client side. If it's 0, your optimistic update has failed.

For more information on SQL Server's concurrency support read the MSDN pages for set transaction isolation level .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM