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.


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

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.


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


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 .


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 ...


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

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

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


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]

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!


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


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


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


0

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

SELECT 151
RETURN -151

Então use:

SELECT 151
ROLLBACK
RETURN -151
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.