I am doing some tests to try to understand how snapshot isolation works...and I do not. I have SET ALLOW_SNAPSHOT_ISOLATION ON in my db (not interested in READ_COMMITTED_SNAPSHOT atm). Then I do the following tests. I will mark different sessions (practically different tabs in my ssms) by [s1] and [s2] markup,[s2] being the isolated session, and [s1] simulating another, non-isolated session.
First, make a table, and let's give it a row. #[s1]:
create table _g1 (v int)
insert _g1 select 1
select * from _g1
(Output: 1)
Now let's begin an isolated transaction.
#[s2]:
set transaction isolation level snapshot
begin tran
Insert another row, #[s1]:
insert _g1 select 2
Now let's see what the isolated transaction "sees", #[s2]:
select * from _g1
(Output: 1,2)
Strange. Shouldn't the isolation "start counting" from the moment of the "Begin tran"? Here, it should not have returned the 2....Let's do this another time. #[s1]:
insert _g1 select 3
#[s2]:
select * from _g1
(Output: 1,2)
So, this time it worked as I expected and did not account the latest insert.
How is this behaviour explained? Does the isolation start working after the first access of each table?
Snapshot isolation works with row versioning. For each modification on a row, the database engine maintains the previous and the current version of the row, along with the serial number (XSN) of the transaction that made the modification.
When snapshot isolation is used for a transaction in [s2]:
The Database Engine reads a row within the transaction and retrieves
the row version from tempdb whose sequence number is closest to, and
lower than, the transaction sequence number.
(see "How Snapshot Isolation and Row Versioning Work", in https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/snapshot-isolation-in-sql-server). The transaction sequence number XSN2 for the transaction in [s2] is not assigned until a DML statement is issued.
sys.dm_tran_active_snapshot_database_transactions is a DMV which returns a virtual table
for all active transactions that generate or potentially access row versions. You can query this view to get information about active transactions that access row versions.
To verify all the above, you could try:
#[s1]
create table _g1 (v int)
#[s2]
set transaction isolation level snapshot
begin tran
select * from sys.dm_tran_active_snapshot_database_transactions -- < No XSN has been assigned, yet. Zero rows are returned.
select * from _g1 --< XSN2 is now assigned.
(Output: zero rows)
select * from sys.dm_tran_active_snapshot_database_transactions -- < XSN2 has been assigned and the corresponding record is returned.
#[s1]
insert _g1 select 1
select * from _g1
(Output: 1)
#[s2]
select * from _g1
(Output: zero rows)
Please, see the remarks in https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-tran-active-snapshot-database-transactions-transact-sql?view=sql-server-ver15 about when an XSN is issued:
sys.dm_tran_active_snapshot_database_transactions reports transactions that are assigned a transaction sequence number (XSN). The XSN is assigned when the transaction first accesses the version store. In a database that is enabled for snapshot isolation or read committed isolation using row versioning, the examples show when an XSN is assigned to a transaction:
If a transaction is running under serializable isolation level, an XSN is assigned when the transaction first executes a statement, such as an UPDATE operation, that causes a row version to be created.
If a transaction is running under snapshot isolation, an XSN is assigned when any data manipulation language (DML) statement, including a SELECT operation, is executed.
Therefore, to answer your question, snapshot isolation "starts counting" after the first 'SELECT' or other DML statement issued within the transaction and not immediately after the 'begin trasaction' statement.
You can Set Transaction Isolation Level Snapshot
either on Database level or Session level.
In our example,we have set on Session level.
So Isolation Level Snapshot will work only in that session on which it was declare.
Secondly, you must issue a T-Sql statement.
In #s2 ,
Set Transaction Isolation Level Snapshot
Begin Tran
Here Transaction is open but there is no T-Sql.
So Snapshot Version of which which table will be maintain ?
Set Transaction Isolation Level Snapshot
Begin Tran
select * from _g1
Here isolation level will work on table _g1. or what ever tables are mention in T-Sql within Transaction .
In other word it will maintain Own version of records for all tables in TempDB mention in T-Sql this TRANSACTION.
It will read data from TempDB untill that Transaction is not Commit or Rollback.
After this,it will read data from Table .
In #s2, Begin Tran is without RollBack or Commit.
Though all record are committed in #s1,
it do not fetch 3. it fetch 1,2 which were committed prior to issue T Sql on same table.
If Rollback or Commit is done in #S2 then output will be (1,2,3).
Since all Insert statement in #s1 is committed.
After Transaction is Commit or Rollback, it will read data from .
In other example,
Truncate table _g1.
We first start #s2,
Set Transaction Isolation Level Snapshot
Begin Tran
select * from _g1
Output : no record.
Here database engine has maintain own version for table _g1.
Since there is no record in _g1,so TempDB is empty.
In #s1,
insert _g1 select 1
select * from _g1
(Output: 1)
In #s2,
If you simply only run
select * from _g1
or you run all script
Output is still nothing.Because we have not committed or rollback so it continue reading from TempDB.
After Commit or Rollback, it will again refresh Record of TempDB.
So output in #s2 will be 1
I have the following code in which I have doubt.
Update Statement on Table 1
Update Statement on Table 2
Select Statement which include both the Table 1
Now above code will return to the application. means it is get all function for the application.
I am getting deadlock error in the application frequently.
I have hundred of users which is fetching the same table at a time.
So I have to make sure that untill the completion of update statement select statement will not fire OR how to lock the update statement.
One more doubt that if suppose I am updating the one row & another user has tried to select that table then will he get the deadlock.
(User was trying to select another row which was not in the update statement.)
what will happen for this scenario.
Please help me.
Thanks in advance
You should use transaction,
BEGIN TRANSACTION [Tran1]
BEGIN TRY
Update Statement on Table 1
Update Statement on Table 2
Select Statement which include both the Table 1
COMMIT TRANSACTION [Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1]
END CATCH
GO
If you want nobody to update/delete the row, I would go with the UPDLOCK on the SELECT statement. This is an indication that you will update the same row shortly, e.g.
select #Bar = Bar from oFoo WITH (UPDLOCK) where Foo = #Foo;
I am running delete sql in Managment Studio 2008 and it indicated to me the sql has worked....but it hasnt.
For example
select count(*) from MyTable where [MYkey] =24
returns 1
delete from MyTable where [MYkey] = 24
Rows Affected 1
But if I immediately run the first statement again, the record is still there. If I try an update statement, that works. I am seeing this behaviour on all tables in the database.
I had a few issues with the Transaction log being full a few days ago, I change the recovery model to simple. Could this be related? If so what do I need to do?
Try this -
BEGIN TRANSACTION;
delete from MyTable where [MYkey] = 24;
COMMIT TRANSACTION;
I've got a stored procedure that does the following (Simplified):
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRANSACTION
DECLARE #intNo int
SET #intNo = (SELECT MAX(intNo) + 1 FROM tbl)
INSERT INTO tbl(intNo)
Values (#intNo)
SELECT intNo
FROM tbl
WHERE (intBatchNumber = #intNo - 1)
COMMIT TRANSACTION
My issue is that when two or more users execute this at the same time I am getting deadlocks. Now as I understand it the moment I do my first select in the proc this should create a lock in tbl. If the second procedure is then called while the first procedure is still executing it should wait for it to complete right?
At the moment this is causing a deadlock, any ideas?
The insert query requires a different lock than the select. The lock for select blocks a second insert, but it does not block a second select. So both queries can start with the select, but they both block on the other's insert.
You can solve this by asking the first query to lock the entire table:
SET #intNo = (SELECT MAX(intNo) + 1 FROM tbl with (tablockx))
^^^^^^^^^^^^^^^
This will make the second transaction's select wait for the complete first transaction to finish.
Make it simpler so you have one statement and no transaction
--BEGIN TRANSACTION not needed
INSERT INTO tbl(intNo)
OUTPUT INSERTED.intNo
SELECT MAX(intNo) + 1 FROM tbl WITH (TABLOCK)
--COMMIT TRANSACTION not needed
Although, why aren't you using IDENTITY...?
I have a long running insert transaction that inserts data into several related tables.
When this insert is running, I cannot perform a select * from MainTable. The select just spins its wheels until the insert is done.
I will be performing several of these inserts at the same/overlapping time. To check that the information is not inserted twice, I query the MainTable first to see if an entry is there and that its processed bit is not set.
During the insert transaction, it flips the MainTable processed bit for that row.
So I need to be able to read the table and also be able to tell if the specific row is currently being updated.
Any ideas on how to set this up in Microsoft SQL 2005? I am looking through the SET TRANSACTION ISOLATION LEVEL documentation.
Thank you,
Keith
EDIT: I do not think that the same insert batch will happen at the same time. These are binary files that are being processed and their data inserted into the database. I check that the file has not been processed before I parse and insert the data. When I do the check, if the file has not been seen before I do a quick insert into the MainTable with the processed bit set false.
Is there a way to lock the row being updated instead of the entire table?
You may want to rethink your process before you use READ UNCOMMITTED. There are many good reasons for isolated transactions. If you use READ UNCOMMITTED you may still get duplicates because there is a chance both of the inserts will check for updates at the same time and both not finding them creating duplicates. Try breaking it up into smaller batches or issue periodic COMMITS
EDIT
You can wrap the MainTable update in a transaction that will free up that table quicker but you still may get conflicts with the other tables.
ie
BEGIN TRANSACTION
SELECT #ProcessedBit = ProcessedBit FROM MainTable WHERE ID = XXX
IF #ProcessedBit = False
UPDATE MainTable SET ProcessedBit = True WHERE ID = XXX
COMMIT TRANSACTION
IF #ProcessedBit = False
BEGIN
BEGIN TRANSACTION
-- start long running process
...
COMMIT TRANSACTION
END
EDIT to enable error recovery
BEGIN TRANSACTION
SELECT #ProcessedStatus = ProcessedStatus FROM MainTable WHERE ID = XXX
IF #ProcessedStatus = 'Not Processed'
UPDATE MainTable SET ProcessedBit = 'Processing' WHERE ID = XXX
COMMIT TRANSACTION
IF #ProcessedStatus = 'Not Processed'
BEGIN
BEGIN TRANSACTION
-- start long running process
...
IF No Errors
BEGIN
UPDATE MainTable SET ProcessedStatus = 'Processed' WHERE ID = XXX
COMMIT TRANSACTION
ELSE
ROLLBACK TRANSACTION
END
The only isolation level that allows one transaction to read changes executed by another transaction in progress (before it commits) is:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED