How Serializable works with insert in SQL Server 2005 - sql-server

G'day
I think I have a misunderstanding of serializable. I have two tables (data, transaction) which I insert information into in a serializable transaction (either they are both in, or both out, but not in limbo).
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
INSERT INTO dbo.data (ID, data) VALUES (#Id, data)
INSERT INTO dbo.transactions(ID, info) VALUES (#ID, #info)
COMMIT TRANSACTION
I have a reconcile query which checks the data table for entries where there is no transaction at read committed isolation level.
INSERT INTO reconciles (ReconcileID, DataID)
SELECT Reconcile = #ReconcileID, ID FROM Data
WHERE NOT EXISTS (SELECT 1 FROM TRANSACTIONS WHERE data.id = transactions.id)
Note that the ID is actually a composite (2 column) key, so I can't use a NOT IN operator
My understanding was that the second query would exclude any values written into data without their transaction as this insert was happening at serializable and the read was occurring at read committed.
So what I have seen is that the Reconcile query has picked up data entries that are in the data table but not in the transactions table when entered with this query, which I thought wasn't possible due to the isolation level.

All transaction isolation levels refer exclusively to reads. There is no 'serializable' insert, just as there is no 'read committed' insert. Writes are never affected by the serialization level. Wrapping two inserts into a serialization level, any level, is a no-op since the inserts will behave identically under all isolation levels.
The second query, the INSERT ... SELECT ... on the other hand, by containing a read (the SELECT), is affected by isolation level. The SELECT part will behave according to the current isolation level (in this case, read committed).
Updated
Writes in a transaction are visible outside the transaction only after the commit. If you have a sequence begin transaction; insert into A; insert into B; commit then a reader that is at least at read committed isolation will not see the insert into A before the insert into B. If your reconcile query sees the partial transaction (ie. sees the insert into A w/o a corresponding insert into B) then you have some possible explanations:
the reconcile query is running at the wrong isolation level and does dirty reads
the application did commit the two inserts separately
a code defect in the application that results in inserting only into A
The most likely explanation is the last one.

Related

sql server update blocked by another transaction, concurrency in update

Two SP's are getting executed one after another and the second one is getting blocked by first one. They both are trying to update same table. Two SP's are as following
CREATE PROCEDURE [dbo].[SP1]
Begin
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION ImpSchd
update Table t1 .......... ................................//updating
a set of [n1,n2....n100] records
COMMIT TRANSACTION ImpSchd
SET TRANSACTION ISOLATION LEVEL
READ COMMITTED;
END
2.
CREATE PROCEDURE [dbo].[SP2]
Begin
update Table t1 .......... ................................//updating
a set of [n101,n102.....n200] records
END
My question is when sp1 is running is snapshot level isolation why is it blocking sp2 (n both are updating different set of records)?
If i run first sp for two different set of records simultaneously it
works perfectly.
How can I overcome this situation ?
If using the snapshot level isolation is to be set for each sp updating the same table then it would be a larger change.
if two sp has to update same records in a table, how should i handle that(both sp will update different columns)?
Isolation levels only are for not blocking selects,so any DML wont be affected by Isolation levels.In this case update takes IX lock on table,page followed by taking xlock on row to update.Since you are updating in bulk ,table itself might have been locked due to lock escalation.Hope this helps

SQL Server transaction and SELECT statement

I often saw many people use SELECT statement within a transaction. I often use insert/update/delete only in transaction. I just do not understand that what is the utility of putting a SELECT statement inside transaction.
I got one answer that....SELECT inside the transaction can see changes made by other previous Insert/Update/Delete statements in that transaction, a SELECT statement outside the transaction cannot.
Above statement is it true or not ?
Is this is the only reason that people put SELECT statement inside transaction? Please discuss all the reason in detail if possible. thanks
try doing this and you will understand:
Open a two new queries on SSMS (lets call it A and B from now one) and on A, create a simple table like this:
create table transTest(id int)
insert into transTest values(1)
now, do the following:
do select * from transTest in both of them. You will see the value 1
On A run:
set transaction isolation level read committed
On B run:
begin transaction
insert into transTest values(2)
On A run:
select * from transTest
you will see that the query wont finish because it is locked by the transaction on A
On B run:
commit transaction
Go back to A and you will see that the query finished
Repeat the test with
set transaction isolation level read uncommitted on A
you will see that the query wont be locked by the transaction
One of the main reasons I can think of (the only reason, in fact) is if you want to set a different isolation level, eg:
USE AdventureWorks2008R2;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
SELECT * FROM HumanResources.EmployeePayHistory;
SELECT * FROM HumanResources.Department;
COMMIT TRANSACTION;
For single SELECT statements though, I'm not so sure, unless you had a reason to go the other way and set READ UNCOMMITTED in cases where response time/maximising concurrency is more important than accurate or valid data.
<speculation certainty="75%"> If the single SELECT statement is inside an explicit transaction without altering the isolation levels, I'm pretty sure that will have no effect at all. Individual statements are, by themselves, transactions that are auto-committed or rolled back on error.</speculation>

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.

Understand SQL Read committed & Read uncommitted

I am using SQL Server Express 2008 w/ AdventureWorksLT2008 DB to understand the different between Read committed & Read uncommitted.
According to Wikipedia:
http://en.wikipedia.org/wiki/Isolation_%28database_systems%29
READ COMMITTED
Data records retrieved by a query are
not prevented from modification by
some other transactions.
Assume there is a table named SalesLT.Address and a column AddressLine2 which all rows has blank value
Then i run this query :
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN TRANSACTION
update SalesLT.Address set AddressLine2 = 'new value'
BEGIN TRANSACTION
select AddressLine2 from SalesLT.Address
--Break Here
/*
COMMIT TRANSACTION
COMMIT TRANSACTION
*/
So, you can see the first transaction haven't commited yet, and the second one start to query the data.
It resulting:
So why the second transaction can be retrieved the phantom data even the 1st transaction still not committed?
When data is read inside a transaction, any changes that have been made by that transaction are visible - within that tranasction only (although READ UNCOMMITTED changes this). So above, even though you've started a second, nested, transaction, you're still in scope of the first transaction and can thus read changed data and get 'the changed values'.
Another transaction, on a separate SPID for example, would block if it was using READ COMMITTED and attempted to read this data.

What sql server isolation level should I choose to prevent concurrent reads?

I have the following transaction:
SQL inserts a 1 new record into a table called tbl_document
SQL deletes all records matching a criteria in another table called tbl_attachment
SQL inserts multiple records into the tbl_attachment
Until this transaction finishes, I don't want others users to be aware of the (1) new records in tbl_document, (2) deleted records in tbl_attachment, and (3) modified records in tbl_attachment.
Would Read Committed Isolation be the correct isolation level?
It doesn't matter the transaction isolation level of your writes. What is important is the isolation level of your reads. Normally reads will not see your insert/update/delete until is committed. The only isolation level can does see uncommitted changes is READ UNCOMMITTED. If the concurrent thread uses dirty reads, there is nothing you can do in the write to prevent it.
READ UNCOMMITTED can be either set as an isolation level, or requested explicitly with a (NOLOCK) table hint. Dirty reads can see inconsistent transaction data (eg. the debit does not balance the credit of an operation) and also can cause duplicate reads (read the same row multiple times from a table) and trigger mysterious key violations.
yes, like this:
BEGIN TRANSACTION
insert into tbl_document ...
delete tbl_attachment where ...
inserts into tbl_attachment ...
COMMIT
you may block/lock users until you are finished and commit/rollback the transaction. Also, someone could SELECT your rows from tbl_attachment after your insert into tbl_document but before your delete. If you need to prevent that do this:
BEGIN TRANSACTION
select tbl_attachment with (UPDLOCK,HOLDLOCK) where ...
insert into tbl_document ...
delete tbl_attachment where ...
inserts into tbl_attachment ...
COMMIT
or just delete tbl_attachment before the insert into tbl_document and forget the select with the locking hints.

Resources