2010-01-24 2 views
17

Glauben Sie, es gibt eine bessere Möglichkeit, eine Transaktion in T-SQL zu schreiben? Gibt es einen besseren Ansatz, der die Wartbarkeit und Leistung der Anwendung verbessert, die diese Transaktion verwendet?Schreiben einer Transaktion in T-SQL und Fehlerbehandlung

-- Description: Insert email Receiver under specified subject 
-- ============================================= 
ALTER PROCEDURE [Contact].[Receiver_stpInsert] 
    @First_Name nvarchar(30), 
    @Last_Name nvarchar(30), 
    @Email varchar(60), 
    @Subject_Id int 
AS 
BEGIN 
    SET NOCOUNT ON; 

    DECLARE @error_num int; 


    BEGIN TRANSACTION 

    INSERT INTO Contact.Receiver(First_Name, Last_Name, Email) VALUES(@First_Name, @Last_Name, @Email); 

    SET @error_num = @@ERROR; 
    IF (@error_num <> 0) 
     BEGIN 
      ROLLBACK; 
      RETURN; 
     END 

    DECLARE @rec_record_id int; 
    SET @rec_record_id = (SELECT Record_Id FROM Contact.Receiver WHERE Email = @Email); 

    SET @error_num = @@ERROR; 
    IF (@error_num <> 0) 
     BEGIN 
      ROLLBACK; 
      RETURN; 
     END 

    INSERT INTO Contact.Receiver_Subject(Receiver_Id, Subject_Id) VALUES(@rec_record_id, @Subject_Id); 

    SET @error_num = @@ERROR; 
    IF (@error_num <> 0) 
     BEGIN 
      ROLLBACK; 
      RETURN; 
     END 

    SET @error_num = @@ERROR; 
    IF (@error_num <> 0) 
     BEGIN 
      ROLLBACK; 
      RETURN; 
     END 
    ELSE 
     BEGIN 
      Commit; 

     END 

END 

Antwort

33

Wenn Sie SQL 2005 oder höher verwenden, können Sie den TRY...CATCH Block verwenden, wie folgt aus:

BEGIN TRY 
    BEGIN TRANSACTION; 

    INSERT INTO Contact.Receiver(First_Name, Last_Name, Email) VALUES (@First_Name, @Last_Name, @Email); 
    ... other inserts etc 
    ... 
    COMMIT TRANSACTION; 
END TRY 
BEGIN CATCH 
    IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION; 
END CATCH; 

Auf diese Weise, Sie halten nicht die gleichen Codeblöcke Überprüfung zu wiederholen @ @ERROR.

  • ERROR_NUMBER() gibt die Anzahl der Fehler: Wenn Sie wissen wollen, was Fehler aufgetreten ist, in dem CATCH-Block BEGIN können Sie verschiedene Bits von Informationen erhalten.
  • ERROR_SEVERITY() gibt den Schweregrad zurück.
  • ERROR_STATE() gibt die Fehlerstatusnummer zurück.
  • ERROR_PROCEDURE() gibt den Namen der gespeicherten Prozedur oder den Trigger zurück, in dem der Fehler aufgetreten ist.
  • ERROR_LINE() gibt die Zeilennummer in der Routine zurück, die den Fehler verursacht hat.
  • ERROR_MESSAGE() gibt den vollständigen Text der Fehlermeldung zurück. Der Text enthält die Werte, die für alle ersetzbaren Parameter wie Längen, Objektnamen oder Zeiten angegeben sind.
+13

Ich würde die COMMIT TRANSACTION in den BEGIN TRY .... END TRY Block setzen - nicht nach der ganzen Aussage. Wäre das nicht einfacher und genauer? –

+0

warum nicht auch die 'BEGIN TRANSACTION' nach dem' BEGIN TRY' setzen? –

+0

Das war vor 6 Jahren .... kann mich nicht mehr erinnern, was ich dachte;) Aber ja, ich würde die Transaktion in den BEGINNEN TRY setzen. Ich habe die Antwort aktualisiert. – AdaTheDev

5

Wenn Sie SQL Server 2000 oder vor, dann ja - Überprüfung der @@ERROR Wert ist im Grunde alles, was Sie tun können.

Mit SQL Server 2005 hat Microsoft das TRY ... CATCH-Konstrukt, das es viel einfacher macht:

BEGIN TRY 
    ...... 
    -- your T-SQL code here 
    ...... 
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 

    -- do other steps, if you want 
END CATCH 
2

Wenn Sie SQL 2005 oder höher verwenden sollten Sie die TRY CATCH Ansatz

2

Sie können alles in einen try catch einbinden, und dann müssen Sie den Rollback nur an einer Stelle codieren. Weitere Informationen finden Sie unter this.

4

Vor nicht allzu langer Zeit gefragt. My answer mit einer TRY/CATCH Vorlage

10

Seit langem befürworte ich die Verwendung von TRY/CATCH and nested transactions in stored procedures.

Dieses Muster gibt Ihnen nicht nur die stark vereinfacht die Fehlerbehandlung des try/catch-Block im Vergleich mit der @@ ERROR zu überprüfen, aber es gibt auch Alles-oder-nichts Semantik für Prozedur Anrufungen verschachtelt.

Wenn die Prozedur im Kontext einer Transaktion aufgerufen wird, rollt die Prozedur nur ihre eigenen Änderungen zurück und überlässt dem Aufrufer die Entscheidung, ob die eingebettete Transaktion zurückgesetzt oder ein alternativer Fehlerpfad versucht werden soll.

create procedure [usp_my_procedure_name] 
as 
begin 
    set nocount on; 
    declare @trancount int; 
    set @trancount = @@trancount; 
    begin try 
     if @trancount = 0 
      begin transaction 
     else 
      save transaction usp_my_procedure_name; 

     -- Do the actual work here 

lbexit: 
     if @trancount = 0 
      commit; 
    end try 
    begin catch 
     declare @error int, @message varchar(4000), @xstate int; 
     select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE(); 
     if @xstate = -1 
      rollback; 
     if @xstate = 1 and @trancount = 0 
      rollback 
     if @xstate = 1 and @trancount > 0 
      rollback transaction usp_my_procedure_name; 

     raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ; 
     return; 
    end catch 
end 

Die ziehen Rücken dieses Ansatzes sind:

  • Es ist nicht mit verteilten Transaktionen arbeiten. Da Transaktionssicherungspunkte nicht mit verteilten Transaktionen kompatibel sind, können Sie dieses Muster nicht verwenden, wenn verteilte Transaktionen erforderlich sind. IMHO verteilte Transaktionen sind böse und sollten nie benutzt werden.
  • Es ändert den ursprünglichen Fehler. Dieses Problem ist in TRY/CATCH-Blöcken inhärent und Sie können nichts dagegen tun. Eine Anwendung, die bereit ist, mit den ursprünglichen SQL Server-Fehlercodes (wie 1202, 1205, 2627 usw.) umzugehen, muss geändert werden, um die Fehlercodes in dem obigen 50000-Bereich zu behandeln, der von Transact-SQL-Code, der TRY/CATCH verwendet, ausgelöst wird .

Auch ein Wort der Vorsicht über die Verwendung von SET XACT_ABORT ON. Diese Einstellung führt dazu, dass ein Stapel bei jedem Fehler eine Transaktion abbricht. Das führt dazu, dass TRY/CATCH-Transaktionen grundsätzlich nutzlos werden und ich empfehle, sie zu vermeiden.

+0

Der dritte Nachteil wäre das übermäßige Kopieren und Einfügen ... wünschte wirklich, es gäbe einen Weg, alle Wiederholungen zu vermeiden. – Aaronaught

+1

@Aaronaught: Leider ist das der Stand der Technik in Bezug auf Transact-SQL. Ist einfach nicht eine Sprache, die Code Wiederverwendung und Kürze freundlich ist. Das Generieren des Codes über Werkzeuge (z. B. XSTL + XML) trägt viel dazu bei, dieses Problem zu lösen, indem die sich wiederholende und fehleranfällige Art des Schreibens von T-SQL beseitigt wird. –