Well-known script. I decided to check it out.
Here is my setup script:
CREATE TABLE Deposits(Amount Money, UserID int) INSERT INTO Deposits (Amount, UserID) SELECT 0.0, 123
Here is my test script.
SET TRANSACTION ISOLATION LEVEL Serializable ---------------------------------------- -- Part 1 ---------------------------------------- BEGIN TRANSACTION DECLARE @amount MONEY SET @amount = ( SELECT Amount FROM Deposits WHERE UserId = 123 ) SELECT @amount as Amount ---------------------------------------- -- Part 2 ---------------------------------------- DECLARE @amount MONEY SET @amount = *value from step 1* UPDATE Deposits SET Amount = @amount + 100.0 WHERE UserId = 123 COMMIT SELECT * FROM Deposits WHERE UserID = 123
I loaded this test script into two windows of the query analyzer and completed each part as described in the question.
All readings occur before any writing, so all threads / scripts will read the value 0 in @amount.
Here are the results:
Reading completed
1 T1.@Amount = 0.00 2 T1.@Amount = 0.00 3 Deposits.Amount = 100.00 4 Deposits.Amount = 100.00
Read Uncommitted
1 T1.@Amount = 0.00 2 T1.@Amount = 0.00 3 Deposits.Amount = 100.00 4 Deposits.Amount = 100.00
Repeat reading
1 T1.@Amount = 0.00 (locks out changes by others on Deposit.UserID = 123) 2 T1.@Amount = 0.00 (locks out changes by others on Deposit.UserID = 123) 3 Hangs until step 4. (due to lock in step 2) 4 Deadlock! Final result: Deposits.Amount = 100.00
Serializable
1 T1.@Amount = 0.00 (locks out changes by others on Deposit) 2 T1.@Amount = 0.00 (locks out changes by others on Deposit) 3 Hangs until step 4. (due to lock in step 2) 4 Deadlock! Final result: Deposits.Amount = 100.00
It explains each type that can be used to achieve these results through modeling thinking.
Read Committed and Read Uncommined , both do not block data that has been read with changes by other users. The difference is that reading uncommitted will allow you to see data that has not yet been committed (minus), and will not block your reading if there is data blocked by others against reading (up), which really says the same thing twice.
Repeatable reading and Serializable , both behave as reads committed to reading. To lock, lock data that has been read against modification by other users. The difference is that the serializable blocks are larger than the read line, it also blocks inserts that will enter records that were not previously.
Thus, with repeated reading, you can see new records (called phantom records) in subsequent readings. With serializable, you block the creation of these records until commit.
The above explanations follow from my interpretation of this msdn article.