Liczba transakcji po EXECUTE wskazuje na niezgodną liczbę instrukcji BEGIN i COMMIT. Poprzedni licznik = 1, aktualny licznik = 0

95

Mam Insertprocedurę składowaną, która będzie dostarczać dane Table1i pobierać Column1wartość z Table1oraz wywołać drugą procedurę składowaną, która będzie zasilać Table2.

Ale kiedy wywołuję drugą procedurę składowaną jako:

Exec USPStoredProcName

Otrzymuję następujący błąd:

Liczba transakcji po EXECUTE wskazuje na niezgodną liczbę instrukcji BEGIN i COMMIT. Poprzedni licznik = 1, aktualny licznik = 0.

Przeczytałem odpowiedzi w innych takich pytaniach i nie jestem w stanie stwierdzić, gdzie dokładnie liczba zatwierdzeń jest zepsuta.

Vignesh Kumar A.
źródło
Czy masz jakieś bloki TRY / CATCH w swojej procedurze?
Remus Rusanu
Tak, mam blok TRY / CATCH
Vignesh Kumar A,

Odpowiedzi:

111

Jeśli masz blok TRY / CATCH, prawdopodobną przyczyną jest to, że przechwytujesz wyjątek przerwania transakcji i kontynuujesz. W bloku CATCH należy zawsze sprawdzać XACT_STATE()i obsługiwać odpowiednie przerwane i niezatwierdzone (skazane) transakcje. Jeśli dzwoniący rozpoczyna transakcję, a calee uderza, powiedzmy, w impas (który przerwał transakcję), w jaki sposób odbiorca ma zamiar poinformować dzwoniącego, że transakcja została przerwana i nie powinna kontynuować „normalnej pracy”? Jedynym wykonalnym sposobem jest ponowne zgłoszenie wyjątku, zmuszając dzwoniącego do obsługi sytuacji. Jeśli po cichu połkniesz przerwaną transakcję, a dzwoniący nadal będzie zakładał, że jest nadal w oryginalnej transakcji, tylko chaos może zapewnić (a błąd, który otrzymasz, jest sposobem, w jaki silnik próbuje się chronić).

Polecam zapoznać się z obsługą wyjątków i transakcjami zagnieżdżonymi, które pokazują wzorzec, którego można używać z transakcjami zagnieżdżonymi i wyjątkami:

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) ;
    end catch
end
go
Remus Rusanu
źródło
3
Dzięki za pomoc. Używając Raiserror, znalazłem problem, chodzi o próbę wstawienia wartości NULL do pola NOT NULL
Vignesh Kumar A
Ale walidacja kontroli ograniczenia nie przerwałaby transakcji. Czy wyraźnie wycofujesz się w haczyku, czy używasz xact_abort on?
Remus Rusanu,
Wyraźnie
wycofuję się
2
Wypróbowałem ten wzorzec, ale nadal nie działa - gdy mam transakcję zewnętrzną, ten wzorzec tworzy punkt zapisu iw przypadku błędu krytycznego (transakcja niemożliwa do zrealizowania) wycofuje transakcję zewnętrzną - to nadal powoduje @@ trancount = 1 przed wejściem procedura i @@ trancount = 0 przy wyjściu
sparrow
3
Myślę, że to trochę na połów jest źle: if @xstate = -1 rollback; Patrząc na ten przykład MSDN , powinniśmy nie wycofywania pełną transakcję chyba było nie transakcja zewnętrzna (czyli chyba zrobiliśmy begin tran). Myślę, że procedura powinna być tylko rollbackwtedy, gdybyśmy rozpoczęli transakcję, która rozwiązałaby problem @ wróbla.
Nick
62

Ja też miałem ten problem. Dla mnie powodem było to, że robiłem

return
commit

zamiast

commit
return   

w jednej procedurze składowanej.

seguso
źródło
4
@seguso - to było bardzo pomocne. Dziękuję za udostępnienie. Czasami coś tak po prostu dostaje się pod kurz. Przydarza się najlepszym z nich.
Leo Gurdian
To był problem dla mnie, ale był mniej oczywisty, ponieważ pakowaliśmy kilka wywołań sproc w jedną dużą transakcję za pośrednictwem naszej warstwy dostępu do danych - więc patrząc na sproc, nie można było stwierdzić, że w ogóle była transakcja. Jeśli masz ten problem, upewnij się, że poza samym sprocem nie ma czegoś, co tworzy transakcję. Jeśli tak, możesz w ogóle nie być w stanie użyć instrukcji powrotu w sproc.
EF0
To byłem ja, miałem transakcję i wracałem przed moją transakcją zatwierdzenia w oświadczeniu if / else
Kevin
19

Zwykle dzieje się tak, gdy transakcja jest uruchamiana i albo nie została zatwierdzona, albo nie jest wycofywana.

Jeśli błąd pojawi się w procedurze składowanej, może to zablokować tabele bazy danych, ponieważ transakcja nie została zakończona z powodu błędów w czasie wykonywania przy braku obsługi wyjątków. Możesz użyć obsługi wyjątków, jak poniżej. USTAW XACT_ABORT

SET XACT_ABORT ON
SET NoCount ON
Begin Try 
     BEGIN TRANSACTION 
        //Insert ,update queries    
     COMMIT
End Try 
Begin Catch 
     ROLLBACK
End Catch

Źródło

Amarnath Balasubramanian
źródło
Gdyby tak było, cytowane pytanie / odpowiedź powinno prawdopodobnie oznaczać, że to powinno być oznaczone jako zduplikowane i zamknięte
Mark Schultheiss
10

Należy pamiętać, że w przypadku korzystania z transakcji zagnieżdżonych operacja ROLLBACK wycofuje wszystkie transakcje zagnieżdżone, w tym tę najbardziej zewnętrzną.

Może to, w przypadku użycia w połączeniu z TRY / CATCH, spowodować błąd, który opisałeś. Zobacz więcej tutaj .

niklasolsn
źródło
5

Może to również wystąpić, jeśli procedura składowana napotka błąd kompilacji po otwarciu transakcji (np. Nie znaleziono tabeli, nieprawidłowa nazwa kolumny).

Okazało się, że muszę użyć 2 procedur składowanych, jednej „roboczej” i jednej opakowującej z try / catch, obie z logiką podobną do tej, którą opisał Remus Rusanu. Przechwytywanie procesu roboczego jest używane do obsługi „normalnych” błędów, a przechwytywanie opakowania do obsługi błędów niepowodzenia kompilacji.

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

Błędy, na które nie ma wpływu konstrukcja TRY… CATCH

Następujące typy błędów nie są obsługiwane przez blok CATCH, gdy występują na tym samym poziomie wykonania, co konstrukcja TRY… CATCH:

  • Błędy kompilacji, takie jak błędy składniowe , które uniemożliwiają uruchomienie wsadu.
  • Błędy występujące podczas ponownej kompilacji na poziomie instrukcji, takie jak błędy rozpoznawania nazw obiektów, które występują po kompilacji z powodu odroczonego rozpoznawania nazw.

Mam nadzieję, że pomoże to komuś innemu zaoszczędzić kilka godzin na debugowaniu ...

Justin
źródło
1
Dzięki Justin. Niezła obserwacja. W moim przypadku wykonywałem agregację w ramach aktualizacji, która nie powoduje błędów kompilacji podczas zapisywania SP, ale rzeczywiście miała nieprawidłową składnię - "Agregat może nie pojawić się na liście zestawów instrukcji UPDATE"
kuklei
4

W moim przypadku błąd był spowodowany przez plik RETURNwewnątrz BEGIN TRANSACTION. Więc miałem coś takiego:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Return
 End
commit

i musi to być:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Rollback Transaction ----- THIS WAS MISSING
     Return
 End
commit
Casey Crookston
źródło
2

Dla mnie po obszernym debugowaniu poprawka była prostym brakującym rzutem; instrukcja w catch po wycofaniu. Bez tego ten brzydki komunikat o błędzie jest tym, co otrzymasz.

begin catch
    if @@trancount > 0 rollback transaction;
    throw; --allows capture of useful info when an exception happens within the transaction
end catch
hexpoint
źródło
2

Miałem ten sam komunikat o błędzie, moim błędem było to, że miałem średnik na końcu wiersza COMMIT TRANSACTION

Zsombor Zsuffa
źródło
To takie proste. Ponadto moja sprawa wymagała instrukcji „ROLLBACK” w przypadku, gdy SP nie zostanie w pełni wykonany. Tylko do zamknięcia / zakończenia transakcji.
J Cordero
1

Raz napotkałem ten błąd po pominięciu tego oświadczenia w mojej transakcji.

COMMIT TRANSACTION [MyTransactionName]
Ken Palmer
źródło
1

Moim zdaniem przyjęta odpowiedź to w większości przypadków przesada.

Przyczyną błędu jest często niedopasowanie BEGIN i COMMIT, co wyraźnie wynika z błędu. Oznacza to użycie:

Begin
  Begin
    -- your query here
  End
commit

zamiast

Begin Transaction
  Begin
    -- your query here
  End
commit

pominięcie transakcji po rozpoczęciu powoduje ten błąd!

omostan
źródło
1

Upewnij się, że nie masz wielu transakcji w tej samej procedurze / zapytaniu, z których jedna lub więcej pozostaje niezatwierdzonych.

W moim przypadku przypadkowo miałem w zapytaniu wyrażenie BEGIN TRAN

Sen Alexandru
źródło
1

Może to również zależeć od sposobu wywoływania SP z kodu C #. Jeśli SP zwraca jakąś wartość typu tabeli, wywołaj SP za pomocą ExecuteStoreQuery, a jeśli SP nie zwraca żadnej wartości, wywołaj SP za pomocą ExecuteStoreCommand

Rajan Tikare
źródło
1

Unikaj używania

RETURN

oświadczenie, gdy używasz

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

i

BEGIN, COMMIT & ROLLBACK

instrukcje w procedurach składowanych SQL

Nitin Patwekari
źródło
0

Jeśli masz strukturę kodu podobną do:

SELECT 151
RETURN -151

Następnie użyj:

SELECT 151
ROLLBACK
RETURN -151
Vidyesh
źródło