2016-05-06 20 views
6

Jestem programistą C# uczącym się więcej TSQL. Napisałem skrypt tak:Czy transakcja wymaga próby złapania?

begin transaction 
--Insert into several tables 
end transaction 

Ale powiedziano mi, że nie był to dobry pomysł i użyć czegoś takiego:

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; 
GO 

nie widzę dlaczego Drugim przykładem jest bardziej poprawne . Czy pierwszy nie działałby w ten sam sposób? Wydaje się, że pierwsza z nich zaktualizowałaby wszystkie tabele, lub w ogóle nie? Nie rozumiem, dlaczego sprawdzenie @@TRANCOUNT jest konieczne przed zatwierdzeniem.

+1

Zrobiłbym taki sam argument, jak ty. Dodatkowo zalecany wzorzec try/catch to anty-wzór, który nazywam try/squelch. Przechwytuje i popełnia błąd, a następnie kontynuuje po cichu. To NIE obsługuje błędów, to je tłumi. W związku z tym blok transakcji typu try/catch nie jest wymagany w przypadku transakcji. Zwłaszcza jeśli jesteś w wyzwalaczu, użycie try/catch prawdopodobnie spowoduje znacznie więcej problemów niż kiedykolwiek rozwiąże. –

+2

Jeśli cokolwiek w drugim przykładzie, zatwierdzenie powinno znajdować się w bloku try, a nie po catch ... myślę, że – Kritner

Odpowiedz

4

Tylko Otwórz transakcję, gdy jesteś wewnątrz bloku try i tuż przed faktycznym oświadczeniem, I zatwierdzić od razu, nie czekaj, aż kontrola przejdzie na koniec partii, aby zatwierdzić transakcje.

Gdy jesteś w Try Block i otworzyłeś transakcję, Jeśli coś pójdzie nie tak Kontrola przejdzie do bloku CATCH, Po prostu wycofaj transakcję tam i wykonaj inną obsługę błędów według potrzeb.

Dodałem trochę czeku przed faktycznym wycofaniem transakcji sprawdzającej dla każdej otwartej transakcji przy użyciu funkcji @@ ROWCOUNT. W tym scenariuszu tak naprawdę nie ma sensu. Jest to bardziej przydatne, gdy robisz jakieś sprawdzenia poprawności w swoim bloku try, zanim otworzysz transakcję, taką jak sprawdzanie wartości parametrów i innych rzeczy i zgłaszanie błędu w bloku try, jeśli którakolwiek z kontroli sprawdzania nie powiedzie się, W takim przypadku kontrola przeskoczy do bloku catch nawet bez otwierania transakcji można sprawdzić każdą otwartą transakcję i wycofać, jeśli są jakieś otwarte. W twoim przypadku, tak naprawdę, nie musisz sprawdzać żadnej otwartej transakcji, ponieważ nie wejdziesz do bloku catch, chyba że coś pójdzie nie tak w twojej transakcji.

BEGIN TRY 

    BEGIN TRANSACTION 
    -- Multiple Inserts 
    INSERT INTO.... 
    INSERT INTO.... 
    INSERT INTO.... 
COMMIT TRANSACTION 
    PRINT 'Rows inserted successfully...' 

END TRY 

BEGIN CATCH 
    IF (@@TRANCOUNT > 0) 
    BEGIN 
     ROLLBACK TRANSACTION 
     PRINT 'Error detected, all changes reversed' 
    END 
    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 
END CATCH 
+0

Myślę, że w swoim wyjaśnieniu miałeś na myśli '@@ TranCount', a nie' @@ RowCount'. Wahałbym się też zawrzeć w ogólnym komunikacie _ "wszystkie zmiany były odwrócone" _ ponieważ mogą istnieć "utracone" wartości tożsamości, efekty uboczne spowodowane przez wyzwalacze, .... – HABO

0

Mam dwa umysły na temat SPRÓBUJ ... POŁOWU w T-SQL.

Chociaż jest to potencjalnie przydatne uzupełnienie języka, fakt, że jest on dostępny, nie zawsze jest powodem do jego używania.

Biorąc

DELETE FROM Table WHERE... 

przykład (który zdaję sobie sprawę jest tylko przykładem). Jedynym sposobem, który kiedykolwiek zakończy się niepowodzeniem z błędem, jest to, że kod został poważnie usunięty ze schematu. (na przykład, jeśli ktoś utworzy obcy klucz z Tabelą na jego końcu PK).

Przy prawidłowym testowaniu tego rodzaju niezgodność między kodem a schematem nie powinna nigdy zostać wprowadzona do produkcji. Zakładając, że tak jest, IMHO "nieprzyzwoity", niezapośredniczony komunikat o błędzie, który spowoduje, będzie lepszym wskazaniem, co poszło nie tak, jak "grzeczne" zawinięcie go w instrukcję SELECT, aby powrócić do klienta. (Co może oznaczać wzmianki o try/squelch, o których mowa w SeanLange).

Dla bardziej skomplikowanych scenariuszy, widzę użycie dla TRY ... CATCH. Chociaż IMHO nie zastąpi dokładnego sprawdzania parametrów wejściowych.

1

chcę dać mojej perspektywy tutaj jako C# Developer:

W prostym scenariuszu podanej powyżej (tylko wstawianie na kilka tabel ze skryptu), nie ma powodu, aby dodać try/catch, jak nie przynosi żadnej korzyści transakcji. Oba przykłady dadzą dokładnie taki sam wynik: albo wszystkie tabele zostaną wstawione, albo nie będą. Stan bazy danych pozostaje spójny.(Ponieważ COMMIT TRANSACTION nigdy nie jest wywoływany, wycofanie jest domyślnie wywoływane przez serwer SQL na końcu skryptu.)

Jednak są chwile, kiedy można robić rzeczy w try/catch, które nie są możliwe w zintegrowanym obsługa błędów. Na przykład rejestrowanie błędu w tabeli błędów.

W moim doświadczeniu C#, jedyny czas użycia funkcji Try/Catch to sytuacja, która jest poza kontrolą programisty, na przykład podczas próby otwarcia pliku. W takim przypadku jedynym sposobem na zarządzanie wyjątkiem generowanym przez platformę .Net jest użycie polecenia Try/Catch.

Jeśli robiłem procedurę przechowywaną i chciałem ręcznie sprawdzić stan danych i ręcznie zadzwonić pod numer ROLLBACK TRANSACTION, mogłem to zobaczyć. Ale nadal nie wymagałoby próby/catch.