Nested stored procedures containing TRY CATCH ROLLBACK pattern?

后端 未结 5 2073

I\'m interested in the side effects and potential problems of the following pattern:

CREATE PROCEDURE [Name]
AS
BEGIN
    BEGIN TRANSACTION
    BEGIN TRY
            


        
相关标签:
5条回答
  • 2020-11-22 10:54

    I am not a Linq guy (and neither is Erland), but he wrote the absolute bibles on error handling. Outside of the complications Linq might add to your problem, all of your other questions should be answered here:

    http://www.sommarskog.se/error_handling/Part1.html

    (Old link: http://www.sommarskog.se/error_handling_2005.html)

    0 讨论(0)
  • 2020-11-22 10:55

    -- @Amanda method above doesnt return correct error number

    DECLARE  
      @ErrorMessage   nvarchar(4000),  
      @ErrorSeverity   int,  
      @ErrorState int,  
      @ErrorLine  int,  
      @ErrorNumber   int  
    
    BEGIN TRY  
     SELECT 1/0; -- CATCH me  
    END TRY  
    
    BEGIN CATCH  
    
      DECLARE @err int = @@ERROR  
    
      PRINT @err           -- 8134, divide by zero  
      PRINT ERROR_NUMBER() -- 8134  
    
      SELECT  
        @ErrorMessage  = ERROR_MESSAGE(),  
        @ErrorSeverity = ERROR_SEVERITY(),  
        @ErrorState    = ERROR_STATE(),  
        @ErrorNumber   = ERROR_NUMBER(),  
        @ErrorLine     = ERROR_LINE()  
    
      -- error number = 50000 :(  
      RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine)  
    
    END CATCH  
    
    -- error number = 8134  
    SELECT 1/0
    
    0 讨论(0)
  • 2020-11-22 10:59

    To solve the issue of returning the error number and line number mentioned by @AlexKuznetsov, one can raise the error as such:

    DECLARE @ErrorMessage NVARCHAR(4000)
    DECLARE @ErrorSeverity INT
    DECLARE @ErrorState INT
    DECLARE @ErrorLine INT
    DECLARE @ErrorNumber INT
    
    SELECT @ErrorMessage = ERROR_MESSAGE(),
    @ErrorSeverity = ERROR_SEVERITY(),
    @ErrorState = ERROR_STATE(),
    @ErrorNumber = ERROR_NUMBER(),
    @ErrorLine = ERROR_LINE()
    
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine)
    
    0 讨论(0)
  • 2020-11-22 11:11

    This is our template (error logging removed)

    This is designed to handle

    • Paul Randal's article "No such thing as a nested transaction in SQL Server"
    • Error 266
    • Trigger Rollbacks

    Explanations:

    • all TXN begin and commit/rollbacks must be paired so that @@TRANCOUNT is the same on entry and exit

    • mismatches of @@TRANCOUNT cause error 266 because

      • BEGIN TRAN increments @@TRANCOUNT

      • COMMIT decrements @@TRANCOUNT

      • ROLLBACK returns @@TRANCOUNT to zero

    • You can not decrement @@TRANCOUNT for the current scope
      This is what you'd think is the "inner transaction"

    • SET XACT_ABORT ON suppresses error 266 caused by mismatched @@TRANCOUNT
      And also deals with issues like this "SQL Server Transaction Timeout" on dba.se

    • This allows for client side TXNs (like LINQ) A single stored procedure may be part of distributed or XA transaction, or simply one initiated in client code (say .net TransactionScope)

    Usage:

    • Each stored proc must conform to the same template

    Summary

    • So don't create more TXNs than you need

    The code

    CREATE PROCEDURE [Name]
    AS
    SET XACT_ABORT, NOCOUNT ON
    
    DECLARE @starttrancount int
    
    BEGIN TRY
        SELECT @starttrancount = @@TRANCOUNT
    
        IF @starttrancount = 0
            BEGIN TRANSACTION
    
           [...Perform work, call nested procedures...]
    
        IF @starttrancount = 0 
            COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        IF XACT_STATE() <> 0 AND @starttrancount = 0 
            ROLLBACK TRANSACTION;
        THROW;
        --before SQL Server 2012 use 
        --RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
    END CATCH
    GO
    

    Notes:

    • The rollback check is actually redundant because of SET XACT_ABORT ON. However, it makes me feel better, looks odd without, and allows for situations where you don't want it on

    • Remus Rusanu has a similar shell that uses save points. I prefer an atomic DB call and don't use partial updates like their article

    0 讨论(0)
  • 2020-11-22 11:14

    In case no special error handling needed in CATCH except rethrow and stored procs call chain isn't too long it may be suitable to use such simple template:

    create procedure someNestedSP
    as
    SET XACT_ABORT ON
    begin transaction
    -- do some work or call some other similar SP
    commit transaction
    

    It would also rollback root transaction with all "nested" ones in case of any error but the code is shorter and more straightforward than @gbn's solution. Still XACT_ABORT takes care of most issues mentioned there.

    There may be addiotional overhead for transaction nesting but it may be not too high, I guess.

    0 讨论(0)
提交回复
热议问题