简体   繁体   中英

SQL Server 2005: Confusion using XACT_ABORT=ON with TRY…CATCH

I am slightly confused about using XACT_ABORT ON together with a TRY...CATCH construct to try to rollback a transaction in the CATCH block when there is an error in the TRY block.

I have a stored procedure structured liked this (simplified here of course):

CREATE PROCEDURE dbo.usp_clean_and_re_Insert
AS
   SET XACT_ABORT ON;

   BEGIN TRY

      BEGIN TRANSACTION

      -- first clear the table
      DELETE FROM dbo.table1

      -- re-populate the table
      INSERT INTO dbo.table1
      (col1, col2, col3)
      SELECT  1
              ,dbo.fn_DoSomething('20150101')
              ,dbo.fn_DoSomething('20150123')

      COMMIT TRANSACTION

   END TRY

BEGIN CATCH
-- Test XACT_STATE for 0, 1, or -1.
-- If 1, the transaction is committable.
-- If -1, the transaction is uncommittable and should 
--     be rolled back.
-- XACT_STATE = 0 means there is no transaction and
--     a commit or rollback operation would generate an error.

-- Test whether the transaction is uncommittable.
IF (XACT_STATE()) = -1
BEGIN
    PRINT 'The transaction is in an uncommittable state.' +
          ' Rolling back transaction.'
    ROLLBACK TRANSACTION;
END;

-- Test whether the transaction is active and valid.
IF (XACT_STATE()) = 1
BEGIN
    PRINT 'The transaction is committable.' + 
          ' Committing transaction.'
    COMMIT TRANSACTION;   
END;
END CATCH;

So the SP is intended to work like this: if the transaction fails at any point, it should roll back. So when the insert bit fails, the delete bit should be rolled back, ie the table should be in the same state as before.

Now, let's say that at run-time dbo.fn_DoSomething() function is not available (it has been dropped by a DBA by mistake). The SP as written above works as expected, ie the transaction is rolled back and the table remains intact and the error messages displayed in SSMS look like:

" Msg 208, Level 16, State 1, Procedure usp_clean_and_re_Insert, Line 15 Invalid object name 'dbo.fn_DoSOmething'. "

However for some reason the PRINT statements from the CATCH block do not seem to execute, ie I cannot see them in SSMS? The Microsoft documentation on TRY...CATCH says that if errors occur during execution in the TRY block, execution is passed to the CATCH block ( https://msdn.microsoft.com/en-us/library/ms175976(v=sql.90).aspx ).

If, however, I remove the XACT_ABORT ON, things get even stranger:

  1. PRINT statements still do not appear in SSMS

  2. the same errors as above are displayed correctly, ie

" Msg 208, Level 16, State 1, Procedure usp_clean_and_re_Insert, Line 15 Invalid object name 'dbo.fn_DoSOmething'. "

  1. There is a final error which says:

"Msg 266, Level 16, State 2, Procedure usp_clean_and_re_Insert, Line 52 Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 0, current count = 1."

This results in the table being locked until I disconnect SSMS (the query window where the SP has run), after which the table becomes available again with all results intact (so the DB engine must rollback the uncommittable transaction implicitly).

Reading other posts about this error message (such as this one: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0 ), I understand that I need to check XACT_STATE in the CATCH block and roll back uncommittable transactions (which is the same advice from: https://msdn.microsoft.com/en-us/library/ms189797.aspx ), but this is precisely what I have done in the above SP and yet the transaction does not get rolled back (without XACT_ABORT ON) until I disconnect SSMS?

I am confused! In summary:

  1. Why do I not see the PRINT statements in SSMS?

  2. Why does the ROLLBACK TRANSACTION in CATCH block not get executed when XACT_ABORT ON is removed from the stored procedure?

  3. Why use TRY...CACTH at all if XACT_ABORT ON seems to do the job on its own? Ie If I remove Try..catch and leave XACT_ABORT ON it rolbacks the transaction so why would I need TRY CATCH with implicit ROLLBACK TRANSACTION in the catch block?

I think, and I could be wrong, that XACT_ABORT will not work in this case, because you arent actually starting a transaction yet. Your function doesnt exist, which means SQL Server fails your transaction before you even touch the DB. Reading the man page for XACT_STATE, and the example provided, it looks like you have to actually fail on a read/write. Your query doesnt make it that far because the optimizer sees you have a syntax error (non-existant function being called, which you havent created by the time it should be executed).

If you read this page (man page for XACT_STATE) and the example available, it heavily suggested that XACT_STATE is only set once the contraint error is encountered, which only will happen once the change in data is actually attempted:

https://msdn.microsoft.com/en-us/library/ms189797.aspx

As a way to test, you could have profiler watch for transactions on dbo.table1 . I would bet that with XACT_ABORT ON in the query, the delete doesnt happen. With it removed, its likely the optimizer chooses a different execution plan which allows the delete to run.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM