The XACT_STATE function can be used in a CATCH block to decide whether to commit or roll back a transaction.
In the example below, the UPDATE statement raises an error and control is passed to the CATCH block. In the CATCH block, XACT_STATE is used to check that the transaction can be rolled back. In this case, the INSERT statement before the failed UPDATE statement is rolled back and CountyTown remains an empty table.
/* If XACT_ABORT is ON, an error will cause the entire transaction to be terminated and rolled back. It is recommended to set XACT_ABORT ON so that command timeouts won't leave the transaction open. For the purposes of this post XACT_ABORT is set to OFF to show how XACT_STATE can be used to commit part of a transaction. */ SET XACT_ABORT OFF IF OBJECT_ID('CountyTown','U') IS NOT NULL DROP TABLE CountyTown; GO CREATE TABLE CountyTown ( [County] NVARCHAR(10) ,[Town] NVARCHAR(10) ); GO BEGIN TRY BEGIN TRANSACTION; /* succeeds */ INSERT INTO CountyTown VALUES (N'Tipperary',N'Clonmel') ,(N'Clare',N'Ennis'); /* fails - error occurs */ UPDATE CountyTown SET [County] = N'This string is too long'; /* does not execute as control is passed to CATCH block */ INSERT INTO CountyTown VALUES (N'Kerry',N'Tralee'); /* would commit the transaction if an error did not occur */ COMMIT TRANSACTION; END TRY BEGIN CATCH /* rolls back the transaction if this is possible (i.e. if XACT_STATE is not equal to 0) */ IF XACT_STATE() 0 BEGIN PRINT N'Error encountered' PRINT N'Error Msg: '+ERROR_MESSAGE()+N' Line: ' +CAST(ERROR_LINE() AS NVARCHAR(10)) PRINT N'Rolling back transaction' ROLLBACK TRANSACTION; END END CATCH GO
In the following example, XACT_STATE is used to commit the statements that executed before the error occurred; that is, the first INSERT statement is committed and the table CountyTown will contain 2 rows:
County | Town |
---|---|
Tipperary | Clonmel |
Clare | Ennis |
/* If XACT_ABORT is ON, an error will cause the entire transaction to be terminated and rolled back. It is recommended to set XACT_ABORT ON so that command timeouts won't leave the transaction open. For the purposes of this post XACT_ABORT is set to OFF to show how XACT_STATE can be used to commit part of a transaction. */ SET XACT_ABORT OFF IF OBJECT_ID('CountyTown','U') IS NOT NULL DROP TABLE CountyTown; GO CREATE TABLE CountyTown ( [County] NVARCHAR(10) ,[Town] NVARCHAR(10) ); GO BEGIN TRY BEGIN TRANSACTION; /* succeeds */ INSERT INTO CountyTown VALUES (N'Tipperary',N'Clonmel') ,(N'Clare',N'Ennis'); /* fails - error occurs */ UPDATE CountyTown SET [County] = N'This string is too long'; /* does not execute as control is passed to CATCH block */ INSERT INTO CountyTown VALUES (N'Kerry',N'Tralee'); /* would commit the transaction if an error did not occur */ COMMIT TRANSACTION; END TRY BEGIN CATCH /* rolls back transaction if transaction is uncommittable */ IF XACT_STATE() = -1 BEGIN PRINT N'Error encountered' PRINT N'Error Msg: '+ERROR_MESSAGE()+N' Line: ' +CAST(ERROR_LINE() AS NVARCHAR(10)) PRINT N'Rolling back transaction' ROLLBACK TRANSACTION; END /* commits the transaction if transaction is committable */ IF XACT_STATE() = 1 BEGIN PRINT N'Error encountered' PRINT N'Error Msg: '+ERROR_MESSAGE()+N' Line: ' +CAST(ERROR_LINE() AS NVARCHAR(10)) PRINT N'Committing transaction' COMMIT TRANSACTION; END END CATCH GO