Update inside a read uncommitted transaction - sql

I am having an SP with transaction isolation level set as Read Uncommitted.
For Example
Create Procedure TrailSP
AS
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRY
UPDATE TrialTable
SET TrailColumn ='Update'
WHERE TrailID=1
--this is followed by more updates and selects
END TRY
BEGIN CATCH
RETURN -1;
END CATCH
RETURN 0;
what I want to know is that the first update I have given in the SP will it get committed instantly as it executes or will it get committed along with the rest of the logic at the end of Sp.

It will get committed, as any update under any transaction isolation level, when the transaction commits. This has nothing to do with the stored procedure ending.
If the call to your procedure has a transaction, then the commit will occur when that transaction commits.
If the call to your procedure does not have a transaction but the session has enabled implicit transactions then it will commit when the application explicitly commits.
If the call to your procedure does not have a transaction and session has the auto commit transaction behavior (ie. the most common case) then transaction will commit when the UPDATE statement completes.
Enabling READ UNCOMMITTED for an UPDATE is a no-op.

-Any data read inside a READ UNCOMMITTED is data that could disappear because the transaction that wrote it rollback.
-Its also possible to not see some row that have been committed because a transaction that have not yet committed and might never commit deleted it.
-Its also possible for row to be missing or be duplicated because of PageSplit.
Basically anything is possible, so the data read should never be used to compute anything that should be written to the database or you risk corrupting your database.
TLDR: never use READ UNCOMMITTED

Related

Can i return SQL procedure before committing the transaction?

Can i use return statement in a sql transaction procedure?
Sql Procedure
ALTER PROCEDURE [dbo].[uspProcessStudentRecord]
AS
Begin Transaction
insert into dbo.Student(name,address) values('ABC','INDIA');
return;
Commit Transaction
Is it a good practice of writing return inside a Transaction?
Is it a good practice of writing return inside a Transaction?
No. In fact you will get this SQL Server error and the transaction will remain uncommitted:
Transaction count after EXECUTE indicates a mismatching number of
BEGIN and COMMIT statements. Previous count = 0, current count = 1.
When a transaction is started in a stored procedure, the best practice is to COMMIT or ROLLBACK prior to returning. Also, it's a good practice to specify SET XACT_ABORT ON in procs with explicit transactions to avoid inadvertently leaving a transaction open after a timeout.
rollback transaction will undo it.
returning it will do nothing if you didn't commit it first.
But i do not see any point of doing what you are doing now in your statement unless you are going to use a try catch or some other statement.
If you really do want to be able to commit even if the transaction is rolled back (and the only viable reason I can think of is for logging purposes) there are a couple of options open to you:
Use the Service Broker with event notifications link
Use a linked server pointing back to same DB instance without distributed transaction promotion - this will commit in a completely isolated transaction.
But please first reconsider if this is something you really need to do!

Where is the flaw in my trigger to override DELETE with a soft-delete?

I have a trigger created with
CREATE TRIGGER [CantDeleteStuff] ON [dbo].[Stuff]
INSTEAD OF DELETE
AS
BEGIN
ROLLBACK
UPDATE [dbo].[Stuff] SET [Deleted]=1 FROM DELETED WHERE [dbo].[Stuff].[Id] = DELETED.[Id]
END
GO
and I think it's intent is clear. But when I try to delete a row I get the error
The transaction ended in the trigger. The batch has been aborted.
How to fix?
Instead of Delete will replace the Delete with your triggering code.
According to Technet, the Rollback in your trigger is the issue. You can read more here.
A trigger operates as if there were an outstanding transaction in effect when the trigger is executed. This is true whether the statement firing the trigger is in an implicit or explicit transaction.
When a statement begins executing in autocommit mode, there is an implied BEGIN TRANSACTION to allow the recovery of all modifications generated by the statement if it encounters an error. This implied transaction has no effect on the other statements in the batch because it is either committed or rolled back when the statement completes. This implied transaction is still in effect, however, when a trigger is called.
When a trigger executes, an implicit transaction is started. If the trigger completes execution and ##TRANCOUNT = 0, error 3609 occurs and the batch is terminated. If a BEGIN TRANSACTION statement is issued in a trigger, it creates a nested transaction. In this situation, when a COMMIT TRANSACTION statement is executed, the statement will apply only to the nested transaction.
When using ROLLBACK TRANSACTION in a trigger, be aware of the following behavior:
All data modifications made to that point in the current transaction are rolled back, including any that were made by the trigger.
The trigger continues executing any remaining statements after the ROLLBACK statement. If any of these statements modify data, the modifications are not rolled back.
A ROLLBACK in a trigger closes and deallocates all cursors that were declared and opened in the batch containing the statement that fired the trigger. This includes cursors declared and opened in stored procedures called by the batch that fired the trigger. Cursors declared in a batch prior to the batch that fired the trigger are only closed. However, STATIC or INSENSITIVE cursors are left open if:
CURSOR_CLOSE_ON_COMMIT is set OFF.
The static cursor is either synchronous or a fully populated asynchronous cursor.
Instead of using ROLLBACK TRANSACTION, the SAVE TRANSACTION statement can be used to execute a partial rollback in a trigger.
https://technet.microsoft.com/en-us/library/ms187844(v=sql.105).aspx
So just remove the Rollback.

Does TRANSACTION ISOLATIoN LEVEL SERIALIZABLE create READ lock

I can't seem to find a straight answer on what should be a simple question. If I create a transaction in T-SQL and set the ISOLATION LEVEL to SERIALIZABLE, does this create a READ lock on the tables that I am modifying?
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
GO
BEGIN TRANSACTION;
GO
TRUNCATE TABLE TBL_PRODUCTS;
GO
**INSERT RECORDS HERE**
GO
COMMIT TRANSACTION;
GO
TRUNCATE TABLE will acquire a exclusive shema modify lock on the table preventing all users from reading from the table (unless they use TRANSACTION ISOLATION LEVEL READ UNCOMMITTED or WITH(NOLOCK)) and writing to the table (no exceptions for writing). The exclusive lock will be released at COMMIT TRANSACTION.
EDIT: As Martin Smith pointed out in his comment below the truncate table will acquire a schema modify lock. Meaning there are no other user will be able to read or modify the table whatsoever until a commit or rollback has taken place.
Yes, it will lock the table, and these are the rules for serializable:
Statements cannot read data that has been modified but not yet committed by other transactions.
No other transactions can modify data that has been read by the current transaction until the current transaction completes.
Other transactions cannot insert new rows with key values that would fall in the range of keys read by any statements in the current transaction until the current transaction completes.
https://msdn.microsoft.com/en-us/library/ms173763.aspx

SQL Server: serializable level not working

I have the following SP:
CREATE PROCEDURE [dbo].[sp_LockReader]
AS
BEGIN
SET NOCOUNT ON;
begin try
set transaction isolation level serializable
begin tran
select * from teste
commit tran
end try
begin catch
rollback tran
set transaction isolation level READ COMMITTED
end catch
set transaction isolation level READ COMMITTED
END
The table "test" has many values, so "select * from teste" takes several seconds. I run the sp_LockReader at same time in two diferent query windows and the second one starts showing test table contents without the first one terminates.
Shouldn't serializeble level forces the second query to wait?
How do i get the described behaviour?
Thanks
SERIALIZABLE at the most basic means "hold locks for longer". When you SELECT, the held lock is a shared lock which allows other readers.
If you want to block readers, use WITH (TABLOCKX) hint to take an exclusive lock where you don't need SERIALIZABLE. Or XLOCK with SERIALIZABLE
In other words:
SERIALIZABLE = Isolation Level = lock duration, concurrency
XLOCK = mode= sharing/exclusivity
TABLOCK = Granularity = what is locked
TABLOCKX = combined
See this question/answer for more info
A serializable transaction whose output is not affected by other concurrent transactions. In your case, you are SELECTing twice from the table; neither of those transactions changes the result set of the other, so they may both run simultaneously.
Even if one transaction did update the table, this would not necessarily prevent the other from executing, as the database may work from snapshots.
Have a look here for a better explanation than I can provide... http://en.wikipedia.org/wiki/Isolation_%28database_systems%29
Another note here. If you're using XLOCK under a SERIALIZABLE isolation, other transactions with READ COMMITTED isolation will still be able to read XLOCK'ed rows.
To prevent that, use PAGLOCK along with XLOCK.
See here for details
http://support.microsoft.com/kb/324417

Is there a difference between a SELECT statement inside a transaction and one that is outside of it?

Does the default READ COMMITTED isolation level somehow makes the SELECT statement act different inside of a transaction than one that is not in a transaction?
I am using MS SQL.
Yes, the one inside the transaction can see changes made by other previous Insert/Update/delete statements in that transaction; a Select statement outside the transaction cannot.
If all you are asking about is what the Isolation Level does, then understand that all Select statements (hey, all statements of any kind) - are in a transaction. The only difference between one that is explicitly in a transaction and one that is standing on its own is that the one that is standing alone starts its transaction immediately before it executes it, and commits or roll back immediately after it executes;
whereas the one that is explicitly in a transaction can (because it has a Begin Transaction statement) can have other statements (inserts/updates/deletes, whatever) occurring within that same transaction, either before or after that Select statement.
So whatever the isolation level is set to, both selects (inside or outside an explicit transaction) will nevertheless be in a transaction which is operating at that isolation level.
Addition:
The following is for SQL Server, but all databases MUST work in the same way. In SQL Server the Query Processor is always in one of 3 Transaction Modes, AutoCommit, Implicit, or Explicit.
AutoCommit is the default transaction management mode of the SQL Server Database Engine. .. Every Transact-SQL statement is committed or rolled back when it completes. ... If a statement completes successfully, it is committed; if it encounters any error, it is rolled back. This is the default, and is the answer to #Alex's question in the comments.
In Implicit Transaction mode, "... the SQL Server Database Engine automatically starts a new transaction after the current transaction is committed or rolled back. You do nothing to delineate the start of a transaction; you only commit or roll back each transaction. Implicit transaction mode generates a continuous chain of transactions. ..." Note that the italicized snippet is for each transaction, whether it be a single or multiple statement transaction.
The engine is placed in Explicit Transaction mode when you explicitly initiate a transaction with BEGIN TRANSACTION Statement. Then, every statement is executed within that transaction until you explicitly terminate the transaction (with COMMIT or ROLLBACK) or if a failure occurs that causes the engine to terminate and Rollback.
Yes, there is a bit of a difference. For MySQL, the database doesn't actually start with a snapshot until your first query. Therefore, it's not begin that matters, but the first statement within the transaction. If I do the following:
#Session 1
begin; select * from table;
#Session 2
delete * from table; #implicit autocommit
#Session 1
select * from table;
Then I'll get the same thing in session one both times (the information that was in the table before I deleted it). When I end session one's transaction (commit, begin, or rollback) and check again from that session, the table will show as empty.
The READ COMMITTED isolation level is about the records that have been written. It has nothing to do with whether or not this select statement is in a transaction (except for those things written during that same transaction).
If your database (or in mysql, the underlying storage engine of all tables used in your select statement) is transactional, then there simply no way to execute it "outside of a transaction".
Perhaps you meant "run it in autocommit mode", but that is not the same as "not transactional". In the latter case, it still runs in a transaction, it's just that the transaction ends immediately after your statement is finshed.
So, in both cases, during the run, a single select statement will be isolated at the READ COMMITTED level from the other transactions.
Now what this means for your READ COMMITTED transaction isolation level: perhaps surprisingly, not that much.
READ COMMITTED means that you may encounter non-repeatable reads: when running multiple select statements in the same transaction, it is possible that rows that you selected at a certain point in time are modified and comitted by another transaction. You will be able to see those changes when you re-execute the select statement later on in the same pending transaction. In autocommit mode, those 2 select statements would be executed in their own transaction. If another transaction would have modified and committed the rows you selected the first time, you would be able to see those changes just as well when you executed the statement the second time.