What is the scope of isolation in nested transactions in SQL Server? - sql-server

Consider the following SQL:
BEGIN TRAN
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
INSERT Bands
( Name )
SELECT 'Depeche Mode'
UNION
SELECT 'Arcade Fire'
-- I've indented the inner transaction to make it clearer.
BEGIN TRAN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT *
FROM Bands
COMMIT
-- What is the isolation level right here?
UPDATE Bands
SET Name = 'Modest Mouse'
WHERE Name = 'Oddest House'
COMMIT
In sum, we start a transaction and set its isolation level to READ COMMITTED. We then do some random SQL and start another, nested transaction. In this transaction we change the isolation level to READ UNCOMMITTED. We then commit that transaction and return to the other.
Now, my guess is that after the inner commit, the isolation level returns to READ COMMITTED. Is this correct?

You [Bob Probst] are correct. Interestingly, according to the documentation you linked:
If you issue SET TRANSACTION ISOLATION LEVEL in a stored procedure or trigger, when the object returns control the isolation level is reset to the level in effect when the object was invoked. For example, if you set REPEATABLE READ in a batch, and the batch then calls a stored procedure that sets the isolation level to SERIALIZABLE, the isolation level setting reverts to REPEATABLE READ when the stored procedure returns control to the batch.
So, the bottom line here is that SET TRANSACTION ISOLATION LEVEL has procedure affinity, not transaction affinity (as I had thought).
Awesome!

I don't think that is correct.
Refer to the remarks here: Set Transaction
Only one of the isolation level
options can be set at a time, and it
remains set for that connection until
it is explicitly changed.

Related

What is the TRANSACTION ISOLATION LEVEL that is equivalent with (NOLOCK) for all select statements?

I am building a stored procedure for reporting and I am using (NOLOCK) for all select statements.
There is no locking requirement for the scenario I am working on.
I am thinking to change the TRANSACTION ISOLATION LEVEL at the top of the stored procedure, and avoid adding (NOLOCK) to all of the select statements. Is there a TRANSACTION ISOLATION LEVEL that is equivalent with (NOLOCK) when I set it at the top of the store procedures?
TRANSACTION ISOLATION LEVEL : READ UNCOMMITTED
Specifies that statements can read rows that have been modified by
other transactions but not yet committed. Transactions running at the
READ UNCOMMITTED level do not issue shared locks to prevent other
transactions from modifying data read by the current transaction. READ
UNCOMMITTED transactions are also not blocked by exclusive locks that
would prevent the current transaction from reading rows that have been
modified but not committed by other transactions. When this option is
set, it is possible to read uncommitted modifications, which are
called dirty reads. Values in the data can be changed and rows can
appear or disappear in the data set before the end of the transaction.
This option has the same effect as setting NOLOCK on all tables in all
SELECT statements in a transaction. This is the least restrictive of
the isolation levels.
Note : This is not a recommended Isolation level as this can allows dirty reads
If you want to set the ISOLOATION LEVEL to the SP alone then try changing the SP
CREATE PROCEDURE PRC_SP AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
--your statements
END

Lock a row in a table SQL Server

I need to lock a row in a table so no one can read this line while I'm running a procedure. I am using BEGIN TRAN in this procedure. So, this record I'm trying to block is uncommitted during the process.
Is it possible?
Depending on what is the purpose of your stored procedure:
- In case it modifies the mentioned row, you can base on transaction levels
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
--UPDATE/INSERT/DELETE your row here
...
COMMIT TRANSACTION
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
- Use lock hints
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT column1, column2
FROM yourTable WITH (ROWLOCK)
WHERE ID = YourRecordId
...
COMMIT TRANSACTION
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

Fixing upsert issue with TRANSACTION ISOLATION LEVEL REPEATABLE READ?

I have a SQL statement that does an update, and then if the ##ROWCOUNT is 0, it will insert. this is basically a MERGE in SQL 2008. We are running into situations where two threads are failing on the update simultaneously. It will attempt to insert the same key twice in a table. We are using the Default Transaction isolation level, Read Committed. Will changing the level to repeatable reads fix this or do I have to go all the way to Serializable to make this work? Here is some code:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRAN;
UPDATE TableA
SET Duration = #duration
WHERE keyA = #ID
AND keyB = #IDB;
IF ##rowcount = 0
BEGIN
INSERT INTO TableA (keyA,keyB,Duration)
VALUES (#ID,#IDB,#duration);
END
COMMIT TRAN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;";
You would need to go all the way up to SERIALIZABLE.
Under REPEATABLE READ if the row does not exist then both UPDATE statements can run concurrently without blocking each other and proceed to do the insert. Under SERIALIZABLE the range where the row would have been is blocked.
But you should also consider leaving the isolation level at default read committed and putting a unique constraint on keyA,keyB so any attempts to insert a dupe fail with an error.

Does "Set Transaction Level" require begin transaction?

In MS SQL Server, if I use "SET TRANSACTION ISOLATION LEVEL" in a stored procedure, do I need to wrap the select statements in a BEGIN/END TRANSACTION Block? Will the following work as expected?
CREATE PROCEDURE my_sproc AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT * FROM MyTable
END
The TRANSACTION ISOLATION LEVEL setting is a connection-level setting. There's no need to wrap it in a transaction.
That being said, you understand you will have dirty reads and such from this setting?
You can accomplish the same thing on a query-by-query basis by using locking hints such as:
SELECT * FROM MyTable WITH (NOLOCK)
SET TRANSACTION ISOLATION LEVEL is different to BEGIN/COMMIT/ROLLBACK
The first changes the isolation vs concurrency settings
The second defines the atomic "unit of work"
There is no direct link or interaction: different concepts

Transaction Isolation Level Scopes

What are the scoping rules for transaction isolation levels in SQL Server 2005? I know what the different levels mean, but not how to properly apply them outside of a manually run script. I can't find a guide for practical use in production-quality code.
Obviously, the scope begins when you use a command like this:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
But where does it end? If I set the iso level in a stored procedure and then that proc calls another, does the nested proc inherit it? Even better, if I escalate the iso level inside the nested proc is it going to carry back out into the calling proc? Do transaction commands like BEGIN TRAN, ROLLBACK, and COMMIT make any differences?
When a stored proc is called by an application or an agent job do the isolation level changes persist in some way? Do I always have to revert to the default READ COMMITTED at the end of each proc?
I would test it in different situations but I don't know how to read what the current isolation level is set to.
Run the following and see for yourself:
CREATE PROCEDURE dbo.KeepsIsolation
AS
BEGIN
PRINT 'Inside sproc that does not change isolation level';
DBCC USEROPTIONS;
END
GO
CREATE PROCEDURE dbo.ChangesIsolation
AS
BEGIN
PRINT 'Inside sproc that changes isolation level';
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
DBCC USEROPTIONS;
END
GO
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
DBCC USEROPTIONS;
EXEC dbo.KeepsIsolation;
DBCC USEROPTIONS;
EXEC dbo.ChangesIsolation;
-- demonstrates that isolation level restored to REPEATABLE READ after exiting the procedure
DBCC USEROPTIONS;
From MSDN
If you issue SET TRANSACTION ISOLATION LEVEL in a stored procedure or
trigger, when the object returns control the isolation level is reset
to the level in effect when the object was invoked. For example, if
you set REPEATABLE READ in a batch, and the batch then calls a stored
procedure that sets the isolation level to SERIALIZABLE, the isolation
level setting reverts to REPEATABLE READ when the stored procedure
returns control to the batch.
DBCC USEROPTIONS will display the current isolation level, along with all of the other SET options.
From books on line
Only one of the TRANSACTION ISOLATION
LEVEL options can be set at a time,
and it remains set for that connection
until it is explicitly changed. This
becomes the default behavior unless an
optimization option is specified at
the table level in the FROM clause of
the statement.
Isolation level does not roll back with the transaction.
Isolation level stays current even if you call into procedures and functions.
Please note there is an issue with scope of TRANSACTION LEVELS and SqlServer 2012 or earlier when high performance ADO.NET Connection Pooling is used where this can actually persist across closing of the connection:
SQL Server: Isolation level leaks across pooled connections
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/916b3d8a-c464-4ad5-8901-6f845a2a3447/sql-server-2014-reseting-isolation-level?forum=sqldatabaseengine

Resources