--Drop Table Tab1
Begin Transaction TR1;
Save Transaction TR1;
Create Table Tab1(f1 decimal(10,0));
Begin Transaction TR2
Save Transaction TR2
insert into Tab1 values(1);
Begin Transaction TR3;
Save Transaction TR3;
insert into Tab1 values(2);
Begin Try
insert into Tab1 values('OK');
Commit Transaction TR3;
END TRY
BEGIN Catch
print 'catch'
RollBack Transaction TR3;
End Catch
insert into Tab1 values(3);
Commit Transaction TR2
insert into Tab1 values(4);
Commit Transaction TR1;
--Commit Transaction;
select * from Tab1;
Drop Table Tab1
Select @@TRANCount
Error Occures :
Msg 3931, Level 16, State 1, Line 17
The current transaction cannot be committed and cannot be rolled back to a savepoint. Roll back the entire transaction.
How to handle this.
When certain type of errors get raised you cannot rollback to a save point. See Martin Smith's answer to Rollback transaction to savepoint on failing ALTER TABLE … ADD CONSTRAINT. The way you detect this is to test Xact_state()
.
However your problem is somewhat different because you're also trying to use nested transactions. Nested transactions don't really work in SQL as we would expect them to.
- You can only name the outermost transaction. See Transactions (Database Engine)
For example this fails with Cannot roll back TR2. No transaction or savepoint of that name was found.
BEGIN TRANSACTION TR1;
BEGIN TRANSACTION TR2
ROLLBACK TRANSACTION TR2
COMMIT Transaction TR1
From Nesting Transactions
Committing inner transactions is ignored by the SQL Server Database Engine
It is not legal for the transaction_name parameter of a ROLLBACK TRANSACTION statement to refer to the inner transactions of a set of named nested transactions. transaction_name can refer only to the transaction name of the outermost transaction
Paul S. Randal explores this further in A SQL Server DBA myth a day: (26/30) nested transactions are real
The best you can do is use Save points instead and check the Xact_state in your catch and at the end.
BEGIN TRANSACTION tr1;
SAVE TRANSACTION tr2;
CREATE TABLE tab1
(
f1 DECIMAL(10, 0)
);
SAVE TRANSACTION tr3
INSERT INTO tab1
VALUES (1);
SAVE TRANSACTION tr4;
INSERT INTO tab1
VALUES (2);
BEGIN try
-- change the order of the follwoing two lines around to see the difference
INSERT INTO tab1 VALUES (1 / 0); --Results in a rollback to savepoint
INSERT INTO tab1 VALUES ('OK'); --Results in a complete rollback
COMMIT TRANSACTION tr4;
END try
BEGIN catch
IF Xact_state() = -1
BEGIN
PRINT 'rollback transaction no other work can be done'
ROLLBACK TRANSACTION;
END
ELSE
BEGIN
PRINT 'rollback to savepoint'
ROLLBACK TRANSACTION tr4
END
END catch
IF Xact_state() > 0
BEGIN
INSERT INTO tab1
VALUES (3);
INSERT INTO tab1
VALUES (4);
COMMIT TRANSACTION tr1;
SELECT *
FROM tab1;
DROP TABLE tab1
END