Multiple transactions in a single stored procedure - sql

I need to delete data from 2 tables in SQL Server 2008.
I have two tables A and B. I need to put each in a separate transaction.
This is the code I am using.
BEGIN TRANSACTION;
BEGIN TRY
DELETE from A
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
BEGIN TRANSACTION;
BEGIN TRY
DELETE from B
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
Is there any better way to implement multiple transactions and error handling in SQL Server?
I need to put separate transactions for each table.
I am getting an error when one of the transactions is failed.
Transaction count after EXECUTE indicates a mismatching number of
BEGIN and COMMIT statements. the records are not deleting from other
transaction

try this:
BEGIN TRANSACTION;
BEGIN TRY
DELETE from A
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH
BEGIN TRANSACTION;
BEGIN TRY
DELETE from B
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH

Every query is in an implicit transaction, so an explicit transaction for a single query is redundant. A DELETE will either delete all the records or none of them, not some of them and then fail.
If the two delete queries need to succeed or fail together, then the explicit transaction is required.
If these are huge tables, then deleting a few records at a time can be more efficient without an explicit transaction. (Done this many times.) However, if it has to be a single transaction, then putting the separate smaller deletes in a larger transaction will not prevent transaction log bloat. I don't know if it will be faster in this case. In the old days, I remember using a single transaction could require hours rather than minutes to complete the process. In one case, it would never finish...we gave up after several days.

May be you can do this way:
BEGIN TRANSACTION one;
BEGIN TRY
DELETE from A
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION one;
END CATCH
IF ##TRANCOUNT > 0
COMMIT TRANSACTION one;
BEGIN TRANSACTION two;
BEGIN TRY
DELETE from B
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION two;
END CATCH
IF ##TRANCOUNT > 0
COMMIT TRANSACTION two;

Related

Write stored procedure so if one statement fails it should not effect the other?

I have this procedure which basically insert data.
Begin Transaction
Insert into [dbo].Values
(
EQ
)
values
(
#EQ
)
End
--Set #STATUSRet= 'Created'
--Set #ErrorRet= ''
Commit Transaction
End Try
Begin Catch
Set #STATUSRet= 'Failed'
Set #ErrorRet= (Select ERROR_MESSAGE())
Rollback Transaction
End Catch
Now I want to add a piece of code that calls another database server and insert data into the table in that server i.e. remotely. That's ok I will do that but if that fails then that should not effect my current process of inserting the data as I have described above i.e. if the remote data insertion fails it should not effect the prior insert in any way and should return successfully to the calling application behaving like nothing happened.
The default method of controlling transactions is auto-commit:
Any single statement that changes data and executes by itself is
automatically an atomic transaction. Whether the change affects one
row or thousands of rows, it must complete successfully for each row
to be committed. You cannot manually rollback an auto-commit
transaction.
So, if the two inserts are not wrapped in explicit transaction this will be the behavior. If you have more code blocks, then you can use two separate explicit transactions blocks like this:
DECLARE #ExecuteSecondTransaction BIT = 0;
-- local database
BEGIN TRY
BEGIN TRANSACTION;
-- CODE BLOCK GOES HERE
SET #ExecuteSecondTransaction = 1;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION
END;
-- GET ERRORS DETAILS OR THROW ERROR
END CATCH;
-- remote database
IF #ExecuteSecondTransaction = 1
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
-- CODE BLOCK GOES HERE
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION
END;
-- GET ERRORS DETAILS OR THROW ERROR
END CATCH;
END;

Why are these nested SQL Server transactions throwing a mismatch error if there is a rollback?

By running 'Test Errors' I get unexpected results. I thought by checking for ##Trancount it would avoid mismatches. Can anyone help me with a better way to rollback errors? I want to rollback all transactions which are nested. Stored procedures can be both nested and on their own.
alter procedure TestErrors
as
begin
begin try
begin transaction
exec TestErrorsInner;
IF ##TRANCOUNT > 0
commit transaction;
end try
begin catch
IF ##TRANCOUNT > 0
rollback transaction;
select ERROR_MESSAGE();
end catch
end
alter procedure TestErrorsInner
as
begin
begin try
begin transaction
RAISERROR('Test Error',16,1);
IF ##TRANCOUNT > 0
commit transaction;
end try
begin catch
IF ##TRANCOUNT > 0
rollback transaction;
select ERROR_MESSAGE();
end catch
end
Results:
Test Error
Transaction count after EXECUTE indicates a mismatching number of
BEGIN and COMMIT statements. Previous count = 1, current count = 0.
This is because you are catching a transaction in the TestErrors which is not in Active state.
You have already rolled back your transaction in Catch block of TestErrorsInner.
Then again you are trying to do COMMIT/ROLLBACK it in TestErrors. So it is throwing an error.
It is your responsibility to Raise an Error explicitly again in Catch block of TestErrorsInner. So that Error will be the input for Parent SP.
So your TestErrorsInner should be like
ALTER PROCEDURE TESTERRORSINNER
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
RAISERROR('TEST ERROR',16,1);
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
--SELECT ERROR_MESSAGE();
RAISERROR('TEST ERROR in Catch',16,1); --Here Raised
END CATCH
END
Now execute the TestErrors Stored procedure, you won't get that error.
And You can check the Transaction Status with XACT_STATE()
Calling XACT_STATE() will give result of 0 or 1 or -1 (From MSDN)
If 1, the transaction is committable.
If -1, the transaction is uncommittable and should be rolled back.
if XACT_STATE = 0 means there is no transaction and a commit or rollback operation would generate an error.

Rollback an entire stored procedure

I have a stored procedure with multiple update statements.I dont want to use try catch.How can I rollback the stored procedure and get back the original table?
can something like this work -
begin transaction t1
spName
rollback transaction t1
Yes you can wrap everything into a sproc into a transaction
begin tran
exec testproc
commit tran
--rollback tran --for condition
It works fine even for commit as well rollback
If for inside the sproc you need to open another transaction then you need to capture
DECLARE #vTranCount INT = ##TRANCOUNT
--Commit
IF (#vTranCount = 0 AND ##TRANCOUNT <> 0) COMMIT TRANSACTION --Commit if the Tran is created by this sproc
--rollback during catch
IF(#vTranCount = 0 AND ##TRANCOUNT > 0) ROLLBACK TRANSACTION --Rollback if the Tran is created by this sproc

Sql Server nested transaction rollback ##TRANCOUNT

I've got a stored procedure which begins a new transaction for data manipulation. The procedure itself is executed within another transaction.
I have no influence what happens before my procedure. And it could change.
My idea is to check the ##TRANCOUNT before I begin the nested transaction. Then check the ##TRANCOUNT again in catch block and compare it. In no case I want the outer transaction to be rollbacked. So i wonder if i am safe with this code?
thx for your help!
SET #TRANSCOUNTBEFORE = ##TRANCOUNT;
BEGIN TRANSACTION tx;
BEGIN TRY
/* some data manipulation here */
COMMIT TRANSACTION tx;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > #TRANSCOUNTBEFORE ROLLBACK TRANSACTION tx;
/* some error handling here */
END CATCH;

Nested transaction can't be rolled back

I have SQL Server 2008 and want to do such a transaction:
begin transaction oo;
......
begin try
save transaction xx;
alter table ....; -- this will fail
alter table ....;
alter table ....;
end try
begin catch
rollback transaction xx; -- error here
end catch;
......
commit transaction oo;
At rollback transaction xx;, I get the message
3931 The current transaction cannot be committed and cannot be rolled back to a savepoint. Roll back the entire transaction.
What am I doing wrong here?
Update To explain the scenario:
There is a big transaction "oo", which will change the table structures of the database from product version X to product version Y.
In the nested transactions, user-specific-tables should be tried to be changed (= inner transaction).
If an user-specific-table is somehow corrupted, the whole product-upgrade process should not be rolled back.
On the other hand, the user-specific-tables should not be upgraded if something else failed during the main product table upgrade (outer transaction).
Reference
you have to use this line inside CATCH block
ROLLBACK TRANSACTION;
which will rollback all transaction,
when you use this one in your above statement (posted in Q) then it will
give us error
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
for it you have to put this line in TRY block
COMMIT TRANSACTION oo;
then finally your statement like that
BEGIN TRANSACTION oo;
BEGIN TRY
SAVE TRANSACTION xx;
CREATE TABLE test (ID INT); -- this will fail from second time
SELECT 3;
COMMIT TRANSACTION oo;
END TRY
BEGIN catch
ROLLBACK TRANSACTION;
END CATCH;
UPDATE after comment
BEGIN TRY
BEGIN TRANSACTION xx1;
select 1; -- this will always success
COMMIT TRANSACTION xx1;
BEGIN TRANSACTION xx2;
CREATE TABLE test (id int); -- this will fail from second time
COMMIT TRANSACTION xx2;
BEGIN TRANSACTION xx3;
select 3; -- this will fail from second time
COMMIT TRANSACTION xx3;
END TRY
BEGIN catch
ROLLBACK TRANSACTION
END CATCH;