Properly scoped transactions in Stored Procs - sql-server

Suppose I have a stored procedure that manages its own transaction
CREATE PROCEDURE theProc
AS
BEGIN
BEGIN TRANSACTION
-- do some stuff
IF #ThereIsAProblem
ROLLBACK TRANSACTION
ELSE
COMMIT TRANSACTION
END
If I call this proc from an existing transaction, the proc can ROLLBACK the external transaction.
BEGIN TRANSACTION
EXEC theProc
COMMIT TRANSACTION
How do I properly scope the transaction within the stored procedure, so that the stored procedure does not rollback external transactions?

The syntax to do this probably varies by database. But in Transact-SQL what you do is check ##TRANCOUNT to see if you are in a transaction. If you are then you want to create a savepoint, and at the end you can just pass through the end of the function (believing a commit or rollback will happen later) or else rollback to your savepoint.
See Microsoft's documentation on savepoints for more.
Support for savepoints is fairly widespread, but I think the mechanism (assuming there is one) for finding out that you're currently in a transaction will vary quite a bit.

use ##trancount to see if you're already in a transaction when entering

Related

Force rollback to entire script (stored procedure included)

I have a complex script that contains many stored procedures and I need to do a rollback in all cases.
BEGIN TRANSACTION;
INSERT INTO Table1 VALUES(1);
INSERT INTO Table2 VALUES(2);
EXEC storedprocedure1
EXEC storedprocedure2
....
ROLLBACK;
I have not checked all stored procedures inside (if there is or not other transaction).
I ask if there is a way to rollback the entire script (stored procedures included) independently by presence of other transaction inside the stored procedures.
Thanks!
(Assuming SQL Server)
There is no need to check inside those stored procedures.
The ROLLBACK will rollback all the way to the outer-most transaction, including rolling-back all transactions within storedprocedure1 and storedprocedure2, even if those nested transactions are committed within those procedures.
Selecting ##TRANCOUNT will show you that any ROLLBACK sets the transaction count of the session back to 0.
So if you rollback the outer transaction, anyone who may be expecting those nested transactions to commit is going to be disappointed.

Stored procedure with multiple updates, insert and deletes. What happens if last DML command fails?

So, if I have multiple DML commands inside a stored procedure in SQL Server, if the last one fails, will all the other ones rollback? Considering I am not inside a transaction scope!
You need your stored procedure to use TRY CATCH and a TRANSACTION .
BEGIN TRAN
BEGIN TRY
COMMIT TRANSACTION
END TRY
BEGIN CATCH
-- if error, roll back any changes done by any of the SQL statements
ROLLBACK TRANSACTION
END CATCH
Refer to this
http://techfunda.com/howto/192/transaction-in-stored-procedure

commit transaction and end transaction used together

I have come across the following T-SQL:
...
COMMIT TRANSACTION
END TRANSACTION
BEGIN TRANSACTION;
...
What is the difference between COMMIT and END transaction in this case?
END TRANSACTION doesn't exists in SQL Server T-SQL.
The only transaction commands available are BEGIN TRANSACTION, with an optional name, plus COMMIT and ROLLBACK, also with optional name.
END TRANSACTION will give you a syntax error.

How do I use transactions over multiple stored procedures?

Can you start a transaction in one stored procedure and then roll it back or commit it in a nested procedure?
Commit and rollback have different effects
COMMIT decrements ##TRANCOUNT
ROLLBACK pushes it back to zero
This happens because SQL Server does not really support nested transactions.
If you commit or rollback in a nested stored proc (not transaction), then you'll generate error 266 because of a ##TRANCOUNT mismatch on start and entry
The rollback issue can be resolved by using SET XACT_ABORT ON which is "auto rollback" (simply) and suppresses error 266.
The commit issue... you can't as such. However, you can control where it happens by noting ##TRANCOUNT on stored proc entry and committing only if zero.
For correct transaction handling, see my answers here please:
Nested stored procedures containing TRY CATCH ROLLBACK pattern? and Have I to count transactions before rollback one in catch block in T-SQL?
You can't commit it in a nested procedure, but starting a transaction will wrap all nested procedures within it. So the transaction is good for all stored procedures nested within the transaction. In distributed transactions, data integrity even crosses machine boundaries.
http://msdn.microsoft.com/en-us/library/ms188929(v=SQL.90).aspx
You should pair up your BEGIN TRAN and COMMITs in the same SPROC
If you then call another SPROC which also has a transaction, subsequent BEGIN TRAN / COMMIT TRAN pairs will increment and decrement ##Trancount respectively.
The transaction is committed on the 'last' COMMIT TRAN (##Trancount = 1)
However, any ROLLBACK will always roll back the transaction.
MSDN has a good explanation.
Yes, it is possible. With programming languages like C#, when you pass the connection and transaction object with the command. if anything is caught as wrong than rollback the transaction:
string customerConnection = "Connection";
string query = "insert into temp values ('Data2','data1','data2','data3')";
string query2 = "update tempcst set data = 'Hello data'";
SqlConnection myConnection = new SqlConnection(customerConnection);
myConnection.Open();
SqlTransaction myTrans = myConnection.BeginTransaction();
Try{
int result = executeNonQuery(query, myConnection, myTrans, "");
i = executeNonQuery(query2, myConnection, myTrans, "");
myTrans.Commit();}
catch{
myTrans.Rollback();
myConnection.Close();
}

How can I ensure that nested transactions are committed independently of each other?

If I have a stored procedure that executes another stored procedure several times with different arguments, is it possible to have each of these calls commit independently of the others?
In other words, if the first two executions of the nested procedure succeed, but the third one fails, is it possible to preserve the results of the first two executions (and not roll them back)?
I have a stored procedure defined something like this in SQL Server 2000:
CREATE PROCEDURE toplevel_proc ..
AS
BEGIN
...
while #row_count <= #max_rows
begin
select #parameter ... where rownum = #row_count
exec nested_proc #parameter
select #row_count = #row_count + 1
end
END
First off, there is no such thing as a nested transaction in SQL Server
However, you can use SAVEPOINTs as per this example (too long to reproduce here sorry) from fellow SO user Remus Rusanu
Edit: AlexKuznetsov mentioned (he deleted his answer though) that this won't work if a transaction is doomed. This can happen with SET XACT_ABORT ON or some trigger errors.
From BOL:
ROLLBACK TRANSACTION without a
savepoint_name or transaction_name
rolls back to the beginning of the
transaction. When nesting
transactions, this same statement
rolls back all inner transactions to
the outermost BEGIN TRANSACTION
statement.
I also found the following from another thread here:
Be aware that SQL Server transactions
aren't really nested in the way you
might think. Once an explict
transaction is started, a subsequent
BEGIN TRAN increments ##TRANCOUNT
while a COMMIT decrements the value.
The entire outmost transaction is
committed when a COMMIT results in a
zero ##TRANCOUNT. But a ROLLBACK
without a savepoint rolls back all
work including the outermost
transaction.
If you need nested transaction
behavior, you'll need to use SAVE
TRANSACTION instead of BEGIN TRAN and
use ROLLBACK TRAN [savepoint_name]
instead of ROLLBACK TRAN.
So it would appear possible.

Resources