views:

1959

answers:

5

I want to implement an atomic transaction like the following:

BEGIN TRAN A

SELECT id
FROM Inventory
WITH (???)
WHERE material_id = 25 AND quantity > 10

/*
Process some things using the inventory record and
eventually write some updates that are dependent on the fact that
that specific inventory record had sufficient quantity (greater than 10).
*/

COMMIT TRAN A

The problem is that there are other transactions happening that consume quantity from our inventory, so between the time that the record is selected and the updates are written in transaction A that record could become an invalid selection because it's quantity might have been lowered below the threshold in the WHERE clause.

So the question is what locking hints should I use in the WITH clause to prevent the selected inventory record from being changed before I finish my updates and commit the transaction?

EDIT: So thanks to John, a good solution seems to be to set the transaction isolation level to REPEATABLE READ. This will will make sure that "no other transactions can modify data that has been read by the current transaction until the current transaction completes."

A: 

I believe this would be UPDLOCK.

http://www.devx.com/tips/Tip/13134

ILKirk
Keep in mind that locking does not behaive this way in SQL Server 2005 and above. The article is quite dated.
John Sansom
I'm using SQL Server 2005.
Daniel
A: 

WITH UPDLOCK,HOLDLOCK

SQLMenace
Why is HOLDLOCK needed?
Daniel
to make sure that no other process can even read this and it will hold it for the duration of the tran
SQLMenace
UPDLOCK achieves that
gbn
do updlock then open another window and do a select against the same table
SQLMenace
doh! of course. an update lock is not an exclusive lock.Not sure it's needed though... it depends how you read the problem
gbn
+6  A: 

Hi,

You may actually be better off setting the transaction isolation level rather than using a query hint.

The following reference from Books Online provides details of each of the different Isolation levels.

http://msdn.microsoft.com/en-us/library/ms173763.aspx

Here is good article that explains the various types of locking behaviour in SQL Server and provides examples too.

http://www.sqlteam.com/article/introduction-to-locking-in-sql-server

John Sansom
So then would you suggest using REPEATABLE READ isolation level?
Daniel
Yes this will certainly work. For future reference, you may wish to educate yourself regarding the other isolation levels that are available within SQL Server, that use row versioning. This is quite a tough read but valuable knowledge, http://msdn.microsoft.com/en-us/library/ms345124.aspx
John Sansom
Nice answer - we went through all the isolation level docs when setting up our StackOverflow DB.
Jarrod Dixon
The only concern I have is that setting the isolation level will effect all the transactions running in the system. Since this particular transaction will be relatively slow the transaction level will be REPEATABLE READ for a while. So other transactions that don't need really need this protection
Daniel
level might start blocking, and the system will have diminished concurrency.
Daniel
If significant blocking occurs in your environment then you should definately look into using a Row Versioning Isolation levels as this is an issue they are designed to address.
John Sansom
Hi Jarrod, so StackOverflow uses a SQL Server Backend then? I would be interested to hear about the environment.
John Sansom
Setting the transaction level rather than using hints does seem to be a better solution for the most part. If I run into a lot of blocking or some dead locks I'll look into Row Versioning. Thanks John.
Daniel
A: 

table hints

WITH (HOLDLOCK) allows other readers. UPDLOCK as suggested elsewhere is exclusive.

HOLDLOCK will prevent other updates but they may use the data that is updated later.

UPDLOCK will prevent anyone reading the data until you commit or rollback.

Have you looked at sp_getapplock? This would allow you to serialise this code (if it's the only update bit) without UPDLOCK blocking

Edit: The problem lies mainly in this code running in 2 different sessions. With HOLDLOCk or REPEATABLE_READ, the data will be read in the 2nd session before the 1st session update. With UPDLOCK, noone can read the data in any session.

gbn
The problem is not trying to keep Seesion B from reading the data while Session A is executing the transaction. The problem is to keep Session B from writing to the row that Session A's transaction is using in it's calculations, to prevent the calculations from using stale data.
Daniel
holdlock then. Allows read but not write.Or load all data into local vars/table variables
gbn
+1  A: 

MSSQL:

SELECT id
FROM Inventory (UPDLOCK)
WHERE material_id = 25 AND quantity > 10;

http://www.devx.com/tips/Tip/13134



By any chance you're interested with PostgreSQL:

SELECT id
FROM Inventory    
WHERE material_id = 25 AND quantity > 10
FOR UPDATE;
Michael Buen