I need to do some SQL Server transactions (select + insert) using locks to prevent conflicts. I have the following scenario: I have a table whose primary key is an integer but not auto-incremented (legacy, don't ask), therefore its value is determined as follows:
- a select retrieves the maximum ID value from the table
- the ID is incremented by one
- a new record is inserted in the table using the new ID
All this is done in a transaction, the SQL command being as follows:
SELECT @maxvalue = max(MyId) FROM MyTable
IF @maxvalue > 0
SET @maxvalue = @maxvalue + 1
ELSE
SET @maxvalue = 1
INSERT INTO MyTable(MyValue, ...) VALUES(@maxvalue, ...)
This is prone to duplicate IDs is some scenario and people that wrote it put it in on a loop and retried the operation when a duplicate key error occurred. So I change that, removing the loop and setting locks at the transaction level as follows:
SELECT @maxvalue = max(MyId) FROM MyTable WITH (HOLDLOCK, TABLOCKX)
IF @maxvalue > 0
SET @maxvalue = @maxvalue + 1
ELSE
SET @maxvalue = 1
INSERT INTO MyTable(MyValue, ...) VALUES(@maxvalue, ...)
So I specified two table hints, HOLDLOCK
and TABLOCKX
. That looked fine for some databases but for some that had tens of thousands of records in this table this transaction took a lot of time, around 10 minutes. Looking the in SQL Server Activity Monitor I could see the transaction suspended, although after a very long while it was executed successfully.
I then changed the hints to (HOLDLOCK, TABLOCK)
and works just as fast as it was before hints were used.
The problem is I am not sure whether this is the best combination for what I am looking for or something else is more appropriate. I have seen Confused about UPDLOCK, HOLDLOCK and https://www.sqlteam.com/articles/introduction-to-locking-in-sql-server but would appreciate expert opinions.