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();
}
Related
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
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.
USE AdventureWorks;
GO
BEGIN TRANSACTION;
GO
DELETE FROM HumanResources.JobCandidate WHERE JobCandidateID = 10;
DELETE FROM HumanResources.JobCandidate WHERE JobCandidateID = 11;
DELETE FROM HumanResources.JobCandidate WHERE JobCandidateID = 12;
GO
COMMIT TRANSACTION;
GO
What happens if the first delete statement fails? Will the 2nd and 3rd delete statements be executed? The example doesn't have any error handling, will it leave an open transaction in the case of an exception, or will SQL Server rollback the transaction automatically? Open transaction = locked resources, right?
I am deciding whether I must apply TRY...CATCH to stored procedures that use transactions.
I am aware about set xact_abort on, but want to know what happens without it.
Here is what I found in docs - Controlling Transactions (Database Engine):
If an error prevents the successful completion of a transaction, SQL Server automatically rolls back the transaction and frees all resources held by the transaction
However I read in other posts that automatic rollback is not fired.
In your example, without the use of SET XACT_ABORT ON, the transaction will continue and commit even if the first statement fails. In the text you quoted, the key words are if an error **prevents** the successful completion of a transaction, and a DELETE statement failing does not prevent the transaction from completing.
An example of an error that would cause an automatic rollback is if the connection to the database was severed in the middle of a transaction. Further down the MSDN article you referenced says:
If a run-time statement error (such as a constraint violation) occurs
in a batch, the default behavior in the Database Engine is to roll
back only the statement that generated the error. You can change this
behavior using the SET XACT_ABORT statement. After SET XACT_ABORT ON
is executed, any run-time statement error causes an automatic rollback
of the current transaction. Compile errors, such as syntax errors, are
not affected by SET XACT_ABORT.
It's always a good idea to use error handling to catch errors and rollback if needed.
I prefer to control the process manually:
BEGIN TRY
BEGIN TRAN
-- do work
COMMIT
END TRY
BEGIN CATCH
ROLLBACK
RAISERROR (...)
END CATCH
GO
My question: Is it possible to rollback a stored procedure from another stored procedure in SQL Server 2005?
I have SP1 to insert the values into one table and SP2 to insert the values into another table.
So, if any error comes while executing the SP2 I want to rollback the SP1 also.
Please anyone help me to solve my problem.
Thanks,
Bharath
You need to wrap both calls in a single transaction.
if you call them in SQL then this is way, or using a more comprehensive version like the other answer by answered 7 mins ago gbn.
create proc doall as
BEGIN TRY
BEGIN TRAN
EXECUTE SP1
EXECUTE SP1
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
END CATCH;
if you're calling the SPs from another source, such as from a non SQL program, you need to setup the outer transaction using a Microsoft Distributed Transaction Coordinator (MSDTC) service.
Depending on the API you're using you set up the transaction in Code, and then commit and rollback in code, dependant on conditions.
for example in .net you can use the System.Transactions namespace to create distributed transactions.
In the main program
var tran = new System.Transactions.Transaction();
.
.
.
in one piece of code doe a db call (and pass the tran object to the sql connection) so it enlists in the transaction... if it fails - abort the transaction (trans.Rollback())
.
.
.
.
in another piece of code do another db call (and pass the tran object to the sql connection) so it enlists in the transaction... if it fails - abort the transaction (trans.Rollback())
.
.
.
later...
if both pieces of code succeed commit the transaction
This is a good introduction to the this namespace if you're using .net
You need a wrapper stored procedure to manage the transaction.
##TRANCOUNT on entry and exit to a stored procedure must be the same, otherwise you get error 266. So you can't exit SP1 having started a TXN for example.
I assume that SP1 and SP2 ca nbe called standalone so you need a nested transaction.
Then you hit the same error because
BEGIN TRAN adds one to ##TRANCOUNT
COMMIT TRAN subtracts one from ##TRANCOUNT
ROLLBACK makes ##TRANCOUNT zero
So you can still get error 266.
My answer here explains more about it, including nesting, error 266 suppression etc: Nested stored procedures containing TRY CATCH ROLLBACK pattern?
So in your case, you need soemthing like this
CREATE PROCEDURE Wrapper
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE #starttrancount int
BEGIN TRY
SELECT #starttrancount = ##TRANCOUNT
IF #starttrancount = 0
BEGIN TRANSACTION
EXEC SP1
EXEC SP2
IF #starttrancount = 0
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND #starttrancount = 0
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using #ErrorNumber, #ErrorMessage, etc]
END CATCH
GO
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