Le nombre de transactions après EXECUTE indique un nombre incompatible d'instructions BEGIN et COMMIT. Comptage précédent = 1, nombre actuel = 0

95

J'ai une Insertprocédure stockée qui alimentera les données Table1et en obtiendra la Column1valeur Table1et appellera la deuxième procédure stockée qui alimentera le Table2.

Mais quand j'appelle la deuxième procédure stockée comme suit:

Exec USPStoredProcName

J'obtiens l'erreur suivante:

Le nombre de transactions après EXECUTE indique un nombre incompatible d'instructions BEGIN et COMMIT. Compte précédent = 1, nombre actuel = 0.

J'ai lu les réponses à d'autres questions de ce type et je ne suis pas en mesure de trouver exactement où le nombre de validations est perturbé.

Vignesh Kumar A
la source
Avez-vous des blocs TRY / CATCH dans votre procédure?
Remus Rusanu
Oui, j'ai un bloc TRY / CATCH
Vignesh Kumar A

Réponses:

110

Si vous avez un bloc TRY / CATCH, la cause probable est que vous détectez une exception d'abandon de transaction et que vous continuez. Dans le bloc CATCH, vous devez toujours vérifier XACT_STATE()et gérer les transactions avortées et non validables (condamnées) appropriées. Si votre appelant commence une transaction et que l'appelant rencontre, par exemple, une impasse (qui a annulé la transaction), comment l'appelé va-t-il communiquer à l'appelant que la transaction a été annulée et qu'elle ne devrait pas continuer avec le «statu quo»? Le seul moyen possible est de relancer une exception, forçant l'appelant à gérer la situation. Si vous avalez silencieusement une transaction annulée et que l'appelant continue de supposer qu'il est toujours dans la transaction d'origine, seul le chaos peut garantir (et l'erreur que vous obtenez est la façon dont le moteur essaie de se protéger).

Je vous recommande de passer en revue la gestion des exceptions et les transactions imbriquées qui montre un modèle qui peut être utilisé avec des transactions imbriquées et des exceptions:

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
la source
3
Merci de votre aide. En utilisant Raiserror, j'ai trouvé le problème.Il s'agit d'essayer d'insérer une valeur NULL dans le champ NOT NULL
Vignesh Kumar A
Mais une validation de contrôle de contrainte n'annulerait pas la transaction. Êtes-vous explicitement en train de revenir en arrière ou utilisez-vous xact_abort on?
Remus Rusanu
Je reviens explicitement
Vignesh Kumar A
2
J'ai essayé ce modèle mais cela ne fonctionne toujours pas - lorsque j'ai une transaction externe, ce modèle crée un point de sauvegarde et en cas d'erreur critique (transaction non conforme) annule la transaction externe - cela provoque toujours un @@ trancount = 1 avant d'entrer procedure et @@ trancount = 0 en sortant
sparrow
3
Je pense que ce bit dans CATCH est faux: en if @xstate = -1 rollback; regardant cet exemple MSDN , nous ne devrions pas annuler la transaction complète à moins qu'il n'y ait pas de transaction externe (c'est-à-dire, sauf si nous l'avons fait begin tran). Je pense que la procédure ne devrait que rollbacksi nous avons commencé la transaction, ce qui résoudrait le problème de @ sparrow.
Nick
60

J'ai eu ce problème également. Pour moi, la raison était que je faisais

return
commit

au lieu de

commit
return   

dans une procédure stockée.

seguso
la source
4
@seguso - cela a été très utile. Merci pour le partage. Parfois, quelque chose passe si simplement sous la poussière. Arrive au meilleur d'entre eux.
Leo Gurdian
C'était le problème pour moi, mais c'était moins évident car nous enveloppions plusieurs appels sproc dans une seule grosse transaction via notre couche d'accès aux données - donc en regardant le sproc, vous ne pouviez pas dire qu'il y avait une transaction du tout. Si vous rencontrez ce problème, assurez-vous qu'il n'y a pas quelque chose en dehors du sproc lui-même qui crée une transaction. Si tel est le cas, vous ne pourrez peut-être pas du tout utiliser les instructions return dans le sproc.
EF0
C'était moi, j'avais une transaction et je retournais avant ma transaction de validation dans une instruction if / else
Kevin
18

Cela se produit normalement lorsque la transaction est lancée et qu'elle n'est pas validée ou qu'elle n'est pas annulée.

Si l'erreur survient dans votre procédure stockée, cela peut verrouiller les tables de la base de données car la transaction n'est pas terminée en raison d'erreurs d'exécution en l'absence de gestion des exceptions. Vous pouvez utiliser la gestion des exceptions comme ci-dessous. SET XACT_ABORT

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

La source

Amarnath Balasubramanian
la source
Si tel était le cas, la question / réponse citée devrait probablement signifier que celle-ci devrait être marquée comme dupliquée et fermée
Mark Schultheiss
10

Sachez que si vous utilisez des transactions imbriquées, une opération ROLLBACK annule toutes les transactions imbriquées, y compris la plus externe.

Cela peut, avec une utilisation en combinaison avec TRY / CATCH, entraîner l'erreur que vous avez décrite. En savoir plus ici .

Niklasolsn
la source
5

Cela peut également se produire si votre procédure stockée rencontre un échec de compilation après l'ouverture d'une transaction (par exemple, table non trouvée, nom de colonne non valide).

J'ai trouvé que je devais utiliser 2 procédures stockées, une "worker" et une wrapper avec try / catch toutes les deux avec une logique similaire à celle décrite par Remus Rusanu. Le catch worker est utilisé pour gérer les échecs "normaux" et le catch du wrapper pour gérer les erreurs d'échec de compilation.

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

Erreurs non affectées par une construction TRY… CATCH

Les types d'erreurs suivants ne sont pas traités par un bloc CATCH lorsqu'ils se produisent au même niveau d'exécution que la construction TRY… CATCH:

  • Compilez les erreurs, telles que les erreurs de syntaxe , qui empêchent l'exécution d'un lot.
  • Erreurs qui se produisent pendant la recompilation au niveau de l'instruction, telles que les erreurs de résolution de nom d'objet qui se produisent après la compilation en raison de la résolution de nom différée.

Espérons que cela aide quelqu'un d'autre à économiser quelques heures de débogage ...

Justin
la source
1
Merci Justin. Belle observation. Dans mon cas, je faisais un agrégat à l'intérieur d'une mise à jour qui ne produisait pas d'erreurs de compilation lors de la sauvegarde du SP, mais qui était en effet une syntaxe invalide - "Un agrégat peut ne pas apparaître dans la liste d'ensemble d'une instruction UPDATE"
kuklei
4

Dans mon cas, l'erreur était causée par un RETURNà l'intérieur du fichier BEGIN TRANSACTION. Alors j'avais quelque chose comme ça:

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

et il doit être:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Rollback Transaction ----- THIS WAS MISSING
     Return
 End
commit
Casey Crookston
la source
2

Pour moi, après un débogage approfondi, le correctif était un simple lancer manquant; instruction dans le catch après la restauration. Sans lui, ce vilain message d'erreur est ce que vous obtenez.

begin catch
    if @@trancount > 0 rollback transaction;
    throw; --allows capture of useful info when an exception happens within the transaction
end catch
hexpoint
la source
2

J'ai eu le même message d'erreur, mon erreur était que j'avais un point-virgule à la fin de la ligne COMMIT TRANSACTION

Zsombor Zsuffa
la source
C'est aussi simple que ça. De plus, mon cas avait besoin d'une instruction 'ROLLBACK' au cas où le SP ne serait pas entièrement exécuté. Juste pour clôturer / terminer la transaction.
J Cordero
1

J'ai rencontré cette erreur une fois après avoir omis cette déclaration de ma transaction.

COMMIT TRANSACTION [MyTransactionName]
Ken Palmer
la source
1

À mon avis, la réponse acceptée est dans la plupart des cas une exagération.

La cause de l'erreur est souvent une discordance entre BEGIN et COMMIT, comme l'indique clairement l'erreur. Cela signifie utiliser:

Begin
  Begin
    -- your query here
  End
commit

au lieu de

Begin Transaction
  Begin
    -- your query here
  End
commit

l'omission de Transaction après Begin provoque cette erreur!

Omostan
la source
1

Assurez-vous que vous n'avez pas plusieurs transactions dans la même procédure / requête dont une ou plusieurs ne sont pas validées.

Dans mon cas, j'ai accidentellement eu une instruction BEGIN TRAN dans la requête

Sen Alexandru
la source
1

Cela peut également dépendre de la façon dont vous appelez le SP à partir de votre code C #. Si le SP renvoie une valeur de type de table, appelez le SP avec ExecuteStoreQuery, et si le SP ne renvoie aucune valeur, appelez le SP avec ExecuteStoreCommand

Rajan Tikare
la source
1

Évitez d'utiliser

RETURN

déclaration lorsque vous utilisez

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

et

BEGIN, COMMIT & ROLLBACK

instructions dans les procédures stockées SQL

Nitin Patwekari
la source
0

Si vous avez une structure de code de quelque chose comme:

SELECT 151
RETURN -151

Puis utilisez:

SELECT 151
ROLLBACK
RETURN -151
Vidyesh
la source