And yet more about transactions
Further transaction work revolved around the investigation of naming transactions, and naming savepoints and understanding the differences. Again I worked with the theory of nested transactions to try and understand what was going on.
So, stage one - Labelling the two transactions and committing explicit transactions works fine and as expected DELETE FROM Count -- outside of transaction - a known starting point IF @@TRANCOUNT > 0ROLLBACK TRAN -- in case there are any transactions still hanging around BEGIN TRAN OuterTransaction INSERT INTO COUNT VALUES (108) BEGIN TRAN InnerTransaction UPDATE COUNT SET Count = -108 WHERE Count = 108 COMMIT TRAN InnerTransaction -- can commit transaction InnerTransaction - result will be -108 IF @@TRANCOUNT > 0
COMMIT TRAN OuterTransaction IF @@TRANCOUNT > 0
SELECT 'Transactions left hanging.' SELECT * FROM COUNTThis results in one row, with the value -108 and no transactions left hanging.However, if we don't explicitly commit the InnerTransaction, when we commit the OuterTransaction the InnerTransaction is left hanging, so the last SELECT @@TRANCOUNT returns 1 and not 0 DELETE FROM Count -- outside of transaction - a known starting point IF @@TRANCOUNT > 0
ROLLBACK TRAN -- in case there are any transactions still hanging around BEGIN TRAN OuterTransaction INSERT INTO COUNT VALUES (108) BEGIN TRAN InnerTransaction UPDATE COUNT SET Count = -108 WHERE Count = 108 -- No inner commit / rollback IF @@TRANCOUNT > 0
COMMIT TRAN OuterTransaction IF @@TRANCOUNT > 0
SELECT 'Transactions left hanging.' SELECT * FROM COUNTthis results in one row, with the value of -108, but with a transaction left hanging - as we only issued an explicit COMMIT TRAN against the OuterTransactionso we would probably be better to have the OuterTransaction statement which is currently
IF @@TRANCOUNT > 0
COMMIT TRAN OuterTransaction
as
IF @@TRANCOUNT > 0
COMMIT TRAN
i.e. with no explicit reference to the transactionStage Two, attempting to rollback to a named transaction DELETE FROM Count -- outside of transaction - a known starting point IF @@TRANCOUNT > 0
ROLLBACK TRAN -- in case there are any transactions still hanging around BEGIN TRAN OuterTransaction INSERT INTO COUNT VALUES (108) BEGIN TRAN InnerTransaction UPDATE COUNT SET Count = -108 WHERE Count = 108 ROLLBACK TRAN InnerTransaction IF @@TRANCOUNT > 0
COMMIT TRAN OuterTransaction IF @@TRANCOUNT > 0
SELECT 'Transactions left hanging.' SELECT * FROM COUNTwhich results in the following error:
Msg 6401, Level 16, State 1, Line 77
Cannot roll back InnerTransaction. No transaction or savepoint of that name was found.as well a single row, with the value of -108, and a transaction left open.This behaviour confused me for quite a while until, after a bit of research and reading, I found the following definition for ROLLBACK TRAN [transaction_name] transaction_name - Is the name assigned to the transaction on BEGIN TRANSACTION. transaction_name must conform to the rules for identifiers, but only the first 32 characters of the transaction name are used. When nesting transactions, transaction_name must be the name from the outermost BEGIN TRANSACTION statement.So, instead, we can either rollback to the OuterTransaction explicitly DELETE FROM Count -- outside of transaction - a known starting point IF @@TRANCOUNT > 0
ROLLBACK TRAN -- in case there are any transactions still hanging around BEGIN TRAN OuterTransaction INSERT INTO COUNT VALUES (108) BEGIN TRAN InnerTransaction UPDATE COUNT SET Count = -108 WHERE Count = 108 ROLLBACK TRAN OuterTransaction IF @@TRANCOUNT > 0
COMMIT TRAN OuterTransaction IF @@TRANCOUNT > 0
SELECT 'Transactions left hanging.' SELECT * FROM COUNT which now results in no rows - because everything is rolled back in the inner statement - and no hanging transactionsor, in this case, the same effect can be gained from issuing a simple ROLLBACK TRAN : DELETE FROM Count -- outside of transaction - a known starting point IF @@TRANCOUNT > 0
ROLLBACK TRAN -- in case there are any transactions still hanging around BEGIN TRAN OuterTransaction INSERT INTO COUNT VALUES (108) BEGIN TRAN InnerTransaction UPDATE COUNT SET Count = -108 WHERE Count = 108 ROLLBACK TRAN IF @@TRANCOUNT > 0
COMMIT TRAN OuterTransaction IF @@TRANCOUNT > 0
SELECT 'Transactions left hanging.' SELECT * FROM COUNTwhich again results in no rows, and no hanging transactionsStep three, use a save point to restrict the amount of data to be rolled back instead of an inner transaction
DELETE FROM Count -- outside of transaction - a known starting point IF @@TRANCOUNT > 0
ROLLBACK TRAN -- in case there are any transactions still hanging around BEGIN TRAN OuterTransaction INSERT INTO COUNT VALUES (108) SAVE TRAN SavePoint UPDATE COUNT SET Count = -108 WHERE Count = 108 ROLLBACK TRAN SavePoint IF @@TRANCOUNT > 0
COMMIT TRAN IF @@TRANCOUNT > 0
SELECT 'Transactions left hanging.' SELECT * FROM COUNTThis results in one row, with a value of 108, showing that the UPDATE statement has been rolled back leaving just the results of the INSERT statement