Managing Basic Isolation Levels in SQL Server

Mohamed Hendawy
5 min readDec 26, 2023

Introduction:

In the world of relational databases, transactions play a crucial role in maintaining data integrity and consistency. Isolation levels are a key component of transactions, determining the degree to which one transaction is isolated from the effects of other concurrent transactions. SQL Server offers various isolation levels to cater to different requirements and scenarios. This article aims to provide a detailed exploration of the basic isolation levels in SQL Server, accompanied by thorough explanations, practical query examples, and insights into potential anomalies.

Isolation Levels Overview:

SQL Server supports four standard isolation levels as defined by the SQL standard: Read Uncommitted, Read Committed, Repeatable Read, and Serializable. Each level has distinct characteristics, influencing how transactions interact with each other in a multi-user environment. Let’s delve into each isolation level with explanations and practical examples.

-- Syntax for SQL Server and Azure SQL Database

SET TRANSACTION ISOLATION LEVEL
{ READ UNCOMMITTED
| READ COMMITTED
| REPEATABLE READ
| SERIALIZABLE
}
  1. Read Uncommitted:
    The Read Uncommitted isolation level allows transactions to read data that has been modified but not committed by other transactions. While this level offers the highest concurrency, it may lead to issues such as dirty reads, non-repeatable reads, and phantom reads.

Example:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

BEGIN TRANSACTION;

-- Update the current salary of EmployeeID 2
UPDATE Employee
SET Salary = 65000
WHERE EmployeeID = 2;


-- Simultaneous Read by another transaction
SELECT EmployeeID, Salary
FROM Employee
WHERE EmployeeID = 2; --65000

-- ROLLBACK the current transaction
ROLLBACK;

Behavior for Other Transactions:

  • Other transactions can read uncommitted data, potentially encountering inconsistent or incomplete information.
  • The risk of reading uncommitted data from ongoing transactions might affect the accuracy of the results obtained.
  • Concurrent transactions may observe changes that might not be finalized.

2. Read Committed:
Read Committed, the default isolation level, prevents dirty reads but allows non-repeatable reads and phantom reads.
In the Read Committed isolation level, transactions ensure that only committed data is read. However, it allows for non-repeatable reads, where the same read operation within a transaction might yield different results if another transaction commits changes between the reads.

-- Set the isolation level to READ COMMITTED

-- Transaction 1
BEGIN TRANSACTION;

-- Read the current salary of EmployeeID 2
SELECT EmployeeID, Salary
FROM Employee
WHERE EmployeeID = 2;

-- Another transaction updates the salary of EmployeeID 2
UPDATE Employee
SET Salary = 70000
WHERE EmployeeID = 2;

-- Read the salary again
SELECT EmployeeID, Salary
FROM Employee
WHERE EmployeeID = 2;

-- Commit the current transaction
COMMIT;

Behavior:

  • In Transaction 1, two reads of EmployeeID 2 occur.
  • If another transaction commits changes to EmployeeID 2’s salary between the two reads, Transaction 1 will see different results, allowing for a non-repeatable read.
  • Concurrent transactions are shielded from dirty reads but may observe changes between read and update operations.

3. Repeatable Read:
Repeatable Read prevents non-repeatable reads by ensuring that once a value is read, it remains the same for the duration of the transaction. However, it does not prevent phantom reads, where new rows are inserted by other transactions between two reads of the same query within a transaction.

Example:

-- Set the isolation level to REPEATABLE READ

-- Transaction 1
BEGIN TRANSACTION;


SELECT EmployeeID, Salary
FROM Employee


-- Another transaction inserts a new employee with ID 4
INSERT INTO Employee (EmployeeID, Salary)
VALUES (4, 80000);


SELECT EmployeeID, Salary
FROM Employee


-- Commit the current transaction
COMMIT;

Behavior:

  • In Transaction 1, two reads of EmployeeID 2 occur.
  • If another transaction inserts a new employee with ID 4 between the two reads, Transaction 1 will observe a phantom read of the new row, as the result set has changed.
  • Other transactions are blocked from modifying data read by the current transaction.
  • Concurrent transactions attempting to modify data being read are halted.
  • Non-repeatable reads are avoided.
  • Phantom reads may occur if other transactions insert new rows.

4. Serializable:
Serializable provides the highest isolation, eliminating dirty reads, non-repeatable reads, and phantom reads. In this example, we’ll delve into how concurrent transactions interact under the Serializable isolation level.

-- Set the isolation level to SERIALIZABLE

BEGIN TRANSACTION;

-- Read the current salary of EmployeeID 2
SELECT EmployeeID, Salary
FROM Employee
WHERE EmployeeID = 2;

-- Simultaneous UPDATE by another transaction
UPDATE Employee
SET Salary = 65000
WHERE EmployeeID = 2;

-- Commit the current transaction
COMMIT;

Behavior for Other Transactions:

  • Other transactions are blocked from modifying or inserting data that the current transaction is reading.
  • Concurrent transactions attempting to modify or insert data being read are halted.
  • Dirty reads, non-repeatable reads, and phantom reads are all eliminated.

Clear Examples Illustrating Differences:

Consider a scenario with a table named Employee to observe how the same query produces different results under each isolation level.

Scenario:

-- Set the isolation level
SET TRANSACTION ISOLATION LEVEL <Isolation_Level>;

BEGIN TRANSACTION;

-- Read the current salary of EmployeeID 2
SELECT EmployeeID, Salary
FROM Employee
WHERE EmployeeID = 2;

-- Update the salary of EmployeeID 2
UPDATE Employee
SET Salary = 65000
WHERE EmployeeID = 2;

-- Commit the transaction
COMMIT;

Observations:

Read Uncommitted:

  • May result in a dirty read if another transaction modifies the salary before the current transaction commits.
  • Concurrent transactions may observe ongoing changes.

Read Committed:

  • Prevents dirty reads but allows non-repeatable reads.
  • Shields concurrent transactions from ongoing changes but not from committed changes.

Repeatable Read:

  • Prevents non-repeatable reads but allows phantom reads.
  • Concurrent transactions are blocked from modifying read data.
  • Phantom reads may occur if other transactions insert new rows.

Serializable:

  • Provides the highest isolation, eliminating dirty reads, non-repeatable reads, and phantom reads.
  • Concurrent transactions are blocked from modifying or inserting data that the current transaction is reading.

Conclusion:

Selecting the right isolation level is pivotal for balancing performance and data consistency in SQL Server. A nuanced understanding of how each isolation level behaves, backed by clear examples, empowers developers and administrators to make informed decisions tailored to specific application requirements. Striking the ideal balance between data integrity and system performance requires a thoughtful selection of the appropriate isolation level. Moreover, recognizing how each level affects concurrent transactions ensures a harmonious coexistence in a complex database environment.

--

--