2010-05-19 13 views
5

Nie jestem ekspertem od serwera SQL. Czy jest to poprawny wzorzec do obsługi błędów w partii SELECT, INSERT ... w SERWERZE SQI? (Używam v.2008)Wzór obsługi błędów SQL Server

BEGIN TRANSACTION 
    BEGIN TRY 
     -- statement 1 
     -- statement 2 
     -- statement 3 
     COMMIT TRANSACTION 
    END TRY 

    BEGIN CATCH 
     ROLLBACK TRANSACTION 
    END CATCH 

Dzięki

Odpowiedz

6

Używam czegoś takiego:

CREATE PROCEDURE ErrorHandlingPattern 
( @intParam  int 
    ,@varcharParam varchar(10) 
    ,@dateParam  datetime 
) 
AS 

BEGIN TRY 
    SET NOCOUNT ON 
    DECLARE @Rows    int   --store @@ROWCOUNT in this 
      ,@ErrorMsg   varchar(500) --temp string to build the contents of messages passed into RAISERROR calls 
      ,@LogInfo   varchar(5000) --will hold any info necessary for error debugging, append to this throughout the procedure with important info 
      ,@TransactionCount int   

    SELECT @[email protected]@TRANCOUNT 
      ,@LogInfo='@intParam='  +ISNULL(''''+CONVERT(varchar(10), @intParam  )+'''','NULL') 
       +', @varcharParam=' +ISNULL(''''+      @varcharParam +'''','NULL') 
       +', @dateParam=' +ISNULL(''''+CONVERT(varchar(10), @dateParam,121 )+'''','NULL') 
       +'; @@TRANCOUNT=' +ISNULL(''''+CONVERT(varchar(10), @@TRANCOUNT )+'''','NULL') 

    --validate parameters 
    IF @intParam .... 
    BEGIN --logical error 
     SET @ErrorMsg='Error, invalid value for @intParam: '+ISNULL(''''+CONVERT(varchar(10),@intParam)+'''','NULL') 
     RAISERROR(@ErrorMsg,16,1) --send control to the BEGIN CATCH block 
    END 

    IF @TransactionCount=0 --if we are already in a transaction, no need to start another, nesting transactions +rollback=warnings about transaction count not being the same as when the procedure started. 
    BEGIN 
     BEGIN TRANSACTION 
    END 

    --do your work here.... 
    INSERT/UPDATE/DELETE... 
    SELECT @[email protected]@ROWCOUNT 

    IF @Rows!=ExpectedValue 
    BEGIN --logical error 
     SET @ErrorMsg='Error, INSERT/UPDATE/DELETE of tableXYZ resulted in '+ISNULL(''''+CONVERT(varchar(10),@Rows)+'''','NULL')+' rows affected' 
     RAISERROR(@ErrorMsg,16,1) --send control to the BEGIN CATCH block 
    END 

    --append improtant info to log string 
    SET @LogInfo=ISNULL(@LogInfo,'')+'; INSERT/UPDATE/DELETE of tableXYZ resulted in '+ISNULL(''''+CONVERT(varchar(10),@Rows)+'''','NULL')+' rows affected' 

    IF @TransactionCount=0 --only end the transaction if it started here 
    BEGIN 
     COMMIT --put in try block to be able to catch any problems committing 
    END 
END TRY 
BEGIN CATCH 

    IF XACT_STATE()!=0 --if there is any error end the transaction ASAP 
    BEGIN 
     ROLLBACK TRANSACTION 
    END 

    --will echo back the complete original error message 
    DECLARE @ErrorMessage nvarchar(400), @ErrorNumber int, @ErrorSeverity int, @ErrorState int, @ErrorLine int 
    SELECT @ErrorMessage = N'Error %d, Line %d, Message: '+ERROR_MESSAGE(),@ErrorNumber = ERROR_NUMBER(),@ErrorSeverity = ERROR_SEVERITY(),@ErrorState = ERROR_STATE(),@ErrorLine = ERROR_LINE() 
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber,@ErrorLine) 

    --because the transaction was ROLLBACKed this insert will be recorded in the database 
    INSERT INTO YourErrorLog (...) VALUES (...ISNULL(@ErrorMessage,'')+ISNULL(@LogInfo,'')) 

    RETURN 999 

END CATCH 

RETURN 0 
GO 

Ponieważ właśnie robisz partia a batch of SELECT, INSERT, możesz po prostu usunąć CREATE PROCEDURE i deklaracje parametrów i mieć pierwszy początek linii na BEGIN TRY. Ponadto, ponieważ nie tworzysz procedury, zamień wszystkie instrukcje RETURN na GOTO TheEnd i dodaj etykietę TheEnd: u dołu skryptu.

+0

To BARDZO kompletne, dzięki. Rzeczywiście, tworzę procedurę: trochę uprościłem moje pytanie. Bardzo zadowoleni z @@ ROWCOUNT i @@ TRANCOUNT dowiedziałem się dzisiaj. Zawsze unikałem długo zapisanego proca, nazywając bity i kawałki 1 na 1 z Access (które naprawdę opanowałem). Poczuj się jak teraz "pro" ;-) –

3

Prawie:

BEGIN TRANSACTION; 

BEGIN TRY 
    -- Generate a constraint violation error. 
    DELETE FROM Production.Product 
    WHERE ProductID = 980; 
END TRY 
BEGIN CATCH 
    SELECT 
     ERROR_NUMBER() AS ErrorNumber 
     ,ERROR_SEVERITY() AS ErrorSeverity 
     ,ERROR_STATE() AS ErrorState 
     ,ERROR_PROCEDURE() AS ErrorProcedure 
     ,ERROR_LINE() AS ErrorLine 
     ,ERROR_MESSAGE() AS ErrorMessage; 

    IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION; 
END CATCH; 

IF @@TRANCOUNT > 0 
    COMMIT TRANSACTION; 

ten został zaczerpnięty z dokumentacji MSDN na try/catch, gdzie można znaleźć inne przykłady: http://msdn.microsoft.com/en-us/library/ms175976.aspx

+0

Ładne i szybkie. Dzięki –

+1

Zawsze umieszczałem wszystko, łącznie z "BEGIN TRANSACTION" i "COMMIT" w bloku TRY. Czy nie chcesz złapać problemu związanego z tworzeniem lub przeprowadzaniem transakcji? –