A contagem de transações após EXECUTE indica um número incompatível de instruções BEGIN e COMMIT. Contagem anterior = 1, contagem atual = 0

94

Eu tenho um Insertprocedimento armazenado que alimentará dados Table1e obterá o Column1valor de Table1e chamará o segundo procedimento armazenado que alimentará a Tabela2.

Mas quando eu chamo o segundo procedimento armazenado como:

Exec USPStoredProcName

Estou tendo o erro a seguir:

A contagem de transações após EXECUTE indica um número incompatível de instruções BEGIN e COMMIT. Contagem anterior = 1, contagem atual = 0.

Eu li as respostas em outras perguntas e não consigo encontrar exatamente onde a contagem de commits está ficando confusa.

Vignesh Kumar A
fonte
Você tem algum bloco TRY / CATCH em seu procedimento?
Remus Rusanu
Sim, tenho o bloco TRY / CATCH
Vignesh Kumar A

Respostas:

109

Se você tiver um bloco TRY / CATCH, a causa provável é que você esteja capturando uma exceção de anulação de transação e continue. No bloco CATCH você deve sempre verificar oXACT_STATE() transações abortadas e não confirmadas (condenadas) apropriadas e tratá-las. Se o chamador inicia uma transação e o destinatário atinge, digamos, um deadlock (que abortou a transação), como o destinatário se comunicará ao chamador que a transação foi abortada e não deve continuar com o 'business as usual'? A única maneira viável é levantar novamente uma exceção, forçando o chamador a lidar com a situação. Se você silenciosamente engolir uma transação abortada e o chamador continuar presumindo que ainda está na transação original, apenas o caos pode garantir (e o erro que você obtém é a maneira como o mecanismo tenta se proteger).

Recomendo que você repasse o tratamento de exceções e as transações aninhadas, o que mostra um padrão que pode ser usado com transações aninhadas e exceções:

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
fonte
3
Obrigado pela ajuda. Ao usar Raiserror, encontrei o problema. Trata-se de tentar inserir o valor NULL no campo NOT NULL
Vignesh Kumar A
Mas uma validação de verificação de restrição não abortaria a transação. Você está explicitamente revertendo o problema ou usa xact_abort on?
Remus Rusanu
Estou explicitamente
revertendo
2
Eu tentei esse padrão, mas ainda não funciona - quando eu tenho uma transação externa, esse padrão cria um ponto de salvamento e no caso de um erro crítico (transação não editável) reverte a transação externa - isso ainda causa um @@ trancount = 1 antes de entrar procedimento e @@ trancount = 0 ao sair
pardal
3
Eu acho que isso pouco na captura está errado: if @xstate = -1 rollback; Olhando para este exemplo MSDN , devemos não reverter a transação completa a menos que houvesse não uma transação exterior (isto é, a menos que nós fizemos begin tran). Eu acho que o procedimento deveria ser feito apenas rollbackse começássemos a transação, o que resolveria o problema do @parrow.
Nick
59

Eu tive esse problema também. Para mim, o motivo era que eu estava fazendo

return
commit

ao invés de

commit
return   

em um procedimento armazenado.

Seguso
fonte
4
@seguso - isso foi muito útil. Obrigado por compartilhar. Às vezes, algo tão simples fica embaixo da poeira. Acontece com o melhor deles.
Leo Gurdian
Esse era o problema para mim, mas era menos óbvio porque estávamos agrupando várias chamadas sproc em uma grande transação por meio de nossa camada de acesso a dados - então, apenas olhando para o sproc você não poderia dizer que havia uma transação. Se você tiver esse problema, certifique-se de que não haja algo fora do próprio sproc que esteja criando uma transação. Se houver, talvez você não consiga usar instruções de retorno no sproc.
EF0
Este era eu, eu tinha uma transação e estava retornando antes de minha transação de confirmação em uma declaração if / else
Kevin
18

Isso normalmente acontece quando a transação é iniciada e não é confirmada ou não é rollback.

No caso de o erro vir em seu procedimento armazenado, isso pode bloquear as tabelas do banco de dados porque a transação não foi concluída devido a alguns erros de tempo de execução na ausência de tratamento de exceção. Você pode usar o tratamento de exceção como abaixo. 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

Fonte

Amarnath Balasubramanian
fonte
Se este for o caso, a pergunta / resposta citada provavelmente deve significar que esta deve ser marcada como duplicada e fechada
Mark Schultheiss
10

Esteja ciente de que se você usar transações aninhadas, uma operação ROLLBACK reverterá todas as transações aninhadas, incluindo a mais externa.

Isso pode, com o uso em combinação com TRY / CATCH, resultar no erro que você descreveu. Veja mais aqui .

Niklasolsn
fonte
5

Isso também pode ocorrer se o procedimento armazenado encontrar uma falha de compilação após abrir uma transação (por exemplo, tabela não encontrada, nome de coluna inválido).

Eu descobri que tinha que usar 2 stored procedures, uma "worker" e uma wrapper com try / catch, ambas com lógica semelhante à descrita por Remus Rusanu. A captura do trabalhador é usada para tratar as falhas "normais" e a captura do wrapper para tratar os erros de falha de compilação.

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

Erros não afetados por uma construção TRY… CATCH

Os seguintes tipos de erros não são tratados por um bloco CATCH quando ocorrem no mesmo nível de execução que a construção TRY ... CATCH:

  • Erros de compilação, como erros de sintaxe , que impedem a execução de um lote.
  • Erros que ocorrem durante a recompilação em nível de instrução, como erros de resolução de nome de objeto que ocorrem após a compilação devido à resolução de nome adiada.

Espero que isso ajude alguém a economizar algumas horas de depuração ...

Justin
fonte
1
Obrigado Justin. Boa observação. No meu caso, eu estava fazendo um agregado dentro de uma atualização que não produz erros de compilação durante o salvamento do SP, mas era de fato uma sintaxe inválida - "Um agregado pode não aparecer na lista de conjunto de uma instrução UPDATE"
kuklei 01 de
4

No meu caso, o erro estava sendo causado por um RETURNdentro do BEGIN TRANSACTION. Então eu tinha algo assim:

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

e precisa ser:

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

Para mim, depois de uma extensa depuração, a correção foi um simples lance perdido; declaração na captura após a reversão. Sem ele, essa mensagem de erro feia é o que você acaba recebendo.

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

Eu tive a mesma mensagem de erro, meu erro foi que eu tinha um ponto e vírgula no final da linha COMMIT TRANSACTION

Zsombor Zsuffa
fonte
Simples assim. Além disso, meu caso precisava de uma instrução 'ROLLBACK' no caso de o SP não ser totalmente executado. Apenas para fechar / encerrar a transação.
J Cordero
1

Encontrei este erro uma vez depois de omitir esta declaração de minha transação.

COMMIT TRANSACTION [MyTransactionName]
Ken Palmer
fonte
1

Em minha opinião, a resposta aceita é, na maioria dos casos, um exagero.

A causa do erro geralmente é a incompatibilidade de BEGIN e COMMIT conforme claramente declarado pelo erro. Isso significa usar:

Begin
  Begin
    -- your query here
  End
commit

ao invés de

Begin Transaction
  Begin
    -- your query here
  End
commit

omitir Transaction após Begin causa este erro!

Omostan
fonte
1

Certifique-se de não ter várias transações no mesmo procedimento / consulta, das quais uma ou mais não foram confirmadas.

No meu caso, acidentalmente recebi uma instrução BEGIN TRAN na consulta

Sen Alexandru
fonte
1

Isso também pode depender da maneira como você está chamando o SP a partir de seu código C #. Se o SP retornar algum valor de tipo de tabela, invoque o SP com ExecuteStoreQuery, e se o SP não retornar nenhum valor, invoque o SP com ExecuteStoreCommand

Rajan Tikare
fonte
1

Evite usar

RETURN

declaração quando você está usando

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

e

BEGIN, COMMIT & ROLLBACK

instruções em procedimentos armazenados SQL

Nitin Patwekari
fonte
0

Se você tiver uma estrutura de código semelhante a:

SELECT 151
RETURN -151

Então use:

SELECT 151
ROLLBACK
RETURN -151
Vidyesh
fonte