Force rollback to entire script (stored procedure included) - sql-server

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.

Related

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

Rolling back transactions within stored procedures in Transact-SQL

I am new to using Transact-SQL, and I have a question on how transactions within nested stored procedures would be handled.
Consider the following example, where we create an example table as follows:
CREATE TABLE EXAMPLE_TABLE
(
ID INT,
NAME VARCHAR(255)
);
Then, we create a stored procedure with no parameters. This stored procedure involves inserting values into the table from above.
CREATE PROCEDURE SP1
AS
BEGIN
BEGIN TRANSACTION
INSERT INTO EXAMPLE_TABLE (ID, NAME)
VALUES (1, 'BOB')
COMMIT TRANSACTION;
END;
And then we create a second stored procedure with one parameter that calls our first stored procedure.
CREATE PROCEDURE sp2
#EXAMPLE INT
AS
BEGIN
BEGIN TRANSACTION
EXEC SP1
IF (#EXAMPLE < 10)
ROLLBACK TRANSACTION;
ELSE
COMMIT TRANSACTION;
END;
And then we call our second stored procedure as follows:
EXEC sp2 #EXAMPLE = 5;
At the end of this execution, will the values have been added to the EXAMPLE_TABLE? Or does the rollback in the outer stored procedure mean that everything has been rolled back, and nothing committed?
Transactions are scoped, so anything within a transaction is committed/rolled back together. So a value of 5 on your #example variable would prevent records from being added to the EXAMPLE_TABLE. You can check this fiddle for a demo.
I will add that if this example is in anyway similar to actual code you'll be writing, I would suggest to just check the variable value and make a decision on whether or not to run the insert stored procedure in the first place.
The conclusion of Aaron's answer is correct, but the reasoning is a little misleading.
Transactions aren't really "scoped" in the usual way you would think of scoping. The outermost begin tran does of course begin a transaction. But any nested begin tran doesn't really do anything other than increment the ##trancount. Then, when you commit, this doesn't really commit anything unless ##trancount is 1. Only the outermost commit is a "real" commit. Finally, a rollback will rollback everything, not just the current, "most nested" transaction, returning ##trancount to 0. At that point, if you try to commit or rollback you will get an error:
begin tran
print ##trancount
begin tran
print ##trancount
rollback
print ##trancount
commit
1
2
0
Msg 3902, Level 16, State 1, Line 61
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
For this reason, as a stylistic guide when actually coding transactions, I strongly suggest not treating a begin tran as the start of a block which needs to be indented. Treat begin tran, commit and rollback as regular statements, not the start and end of blocks.
The only exception to this behaviour is when you begin a named transaction, in which case you can rollback to the start of that named transaction.

SQL Server Nested transaction in Stored Procedure

I'm using SQL Server 2014; my stored procedure will be nested transaction procedure, whereby it will call few stored procedures that have transaction in them. If either one of the inner stored procedures hits an error, then will rollback all, such as
Begin Try
Begin Tran
Exec Stored Proc 1 (with begin tran inside)
Exec Stored Proc 2 (with begin tran inside)
Exec Stored Proc 3 (with begin tran inside)
Exec Stored Proc 4 (with begin tran inside)
Exec Stored Proc 5 (with begin tran inside)
Commit Tran
End Try
Begin Catch
Catch exception then roll back tran
End Catch
The problem is the transaction count after execute the inner stored procedures are mismatched, however if I didn't open a transaction in the inner stored procedure, it won't rollback. Can anyone give me some suggestions?
Committing inner transactions is ignored by the SQL Server Database Engine. The transaction is either committed or rolled back based on the action taken at the end of the outermost transaction. If the outer transaction is committed, the inner nested transactions are also committed.
Nesting Transactions

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.

Properly scoped transactions in Stored Procs

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

Resources