SQL Server - as transações são revertidas com erro?

192

Temos um aplicativo cliente que está executando algum SQL em um SQL Server 2005, como o seguinte:

BEGIN TRAN;
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
COMMIT TRAN;

É enviado por um comando de cadeia longa.

Se uma das inserções falhar, ou qualquer parte do comando falhar, o SQL Server reverterá a transação? Se não reverter, tenho que enviar um segundo comando para revertê-lo?

Posso fornecer informações específicas sobre a API e o idioma que estou usando, mas acho que o SQL Server deve responder o mesmo para qualquer idioma.

jonathanpeppers
fonte

Respostas:

204

Você pode colocar set xact_abort onantes da transação para garantir que o sql seja revertido automaticamente em caso de erro.

Greg B
fonte
1
Isso funcionará no MS SQL 2K e superior? Essa parece a solução mais simples.
21711 jonathanpeppers
1
Ele aparece nos documentos de 2000, 2005 e 2008, portanto presumo que sim. Estamos usando-o em 2008.
8
Preciso desligá-lo ou é por sessão?
Marc
5
@Marc o escopo xact_abortestá no nível da conexão.
Keith
2
@AlexMcMillan A instrução DROP PROCEDURE modifica a estrutura do banco de dados, diferentemente do INSERT, que funciona apenas com os dados. Portanto, não pode ser agrupado em uma transação. Eu sou simplista demais, mas basicamente é assim que é.
eksortso
195

Você está certo de que toda a transação será revertida. Você deve emitir o comando para recuperá-lo.

Você pode agrupar isso em um TRY CATCHbloco da seguinte maneira

BEGIN TRY
    BEGIN TRANSACTION

        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);
        INSERT INTO myTable (myColumns ...) VALUES (myValues ...);

    COMMIT TRAN -- Transaction Success!
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN --RollBack in case of Error

    -- you can Raise ERROR with RAISEERROR() Statement including the details of the exception
    RAISERROR(ERROR_MESSAGE(), ERROR_SEVERITY(), 1)
END CATCH
Raj More
fonte
2
Eu gosto mais da solução da DyingCactus, a sua é uma linha de código a ser alterada. Se o seu se, por algum motivo, melhor (ou mais confiável) me avise.
jonathanpeppers
13
A tentativa de captura permite capturar (e possivelmente corrigir) o erro e gerar uma mensagem de erro personalizada, se necessário.
21499 Raj Mais
10
"Capturar e registrar" com mais frequência do que "capturar e corrigir", eu pensaria.
quillbreaker
24
A sintaxe de RAISERROR está incorreta pelo menos no SQL Server 2008R2 e posterior. Consulte msdn.microsoft.com/en-us/library/ms178592.aspx para obter a sintaxe correta.
Eric J.
2
@BornToCode Para garantir que a transação exista. Digamos que você reverteu sua transação sob determinada condição (no try), mas o código falha depois. Não há mais transações, mas você ainda está entrando no catch.
Gabriel GM
42

Aqui está o código para obter a mensagem de erro trabalhando com o MSSQL Server 2016:

BEGIN TRY
    BEGIN TRANSACTION 
        -- Do your stuff that might fail here
    COMMIT
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRAN

        DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
        DECLARE @ErrorSeverity INT = ERROR_SEVERITY()
        DECLARE @ErrorState INT = ERROR_STATE()

    -- Use RAISERROR inside the CATCH block to return error  
    -- information about the original error that caused  
    -- execution to jump to the CATCH block.  
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH
samwise
fonte
1
Eu tive que usar DECLARE @Var TYPE; SET @Var = ERROR;para gerar erros no sql server 2005. Caso contrário, o código acima para gerar erros também funciona para bancos de dados mais antigos. Tentar atribuir um valor padrão a uma variável local é o que estava causando o problema.
Jtlindsey #
Você pode usar um simples THROW; em vez das declarações RAISERROR e ERROR_ *.
Rodzmkii 18/10/19
21

Do artigo MDSN, Controlando transações (Mecanismo de Banco de Dados) .

Se um erro de instrução em tempo de execução (como uma violação de restrição) ocorrer em um lote, o comportamento padrão no Mecanismo de Banco de Dados é reverter apenas a instrução que gerou o erro. Você pode alterar esse comportamento usando a instrução SET XACT_ABORT. Após a execução de SET XACT_ABORT ON, qualquer erro de instrução em tempo de execução causa uma reversão automática da transação atual. Erros de compilação, como erros de sintaxe, não são afetados pelo SET XACT_ABORT. Para obter mais informações, consulte SET XACT_ABORT (Transact-SQL)

No seu caso, ele reverterá a transação completa quando alguma das inserções falhar.

Vitaly
fonte
3
o que precisamos para lidar com erros de sintaxe? ou compilar erros? se alguém deles acontece transação inteira deve ser revertida
MonsterMMORPG
Capturar erros de compilação / sintaxe é para isso que servem os projetos SSDT. :-)
Joe, o codificador
10

Se uma das inserções falhar, ou qualquer parte do comando falhar, o SQL Server reverterá a transação?

Não, não tem.

Se não reverter, tenho que enviar um segundo comando para revertê-lo?

Claro, você deve emitir em ROLLBACKvez de COMMIT.

Se você quiser decidir se deve confirmar ou reverter a transação, remova a COMMITfrase da instrução, verifique os resultados das inserções e emita um COMMITou outroROLLBACK dependendo dos resultados da verificação.

Quassnoi
fonte
Portanto, se eu receber um erro, diga "Conflito na chave primária", preciso enviar uma segunda chamada para reverter? Eu acho que isso faz sentido. O que acontece se houver um erro relacionado à rede, como a conexão, durante uma instrução SQL de execução muito longa?
22610 jonathanpeppers
2
Quando uma conexão atinge o tempo limite, o protocolo de rede subjacente (por exemplo, Named Pipesou TCP) interrompe a conexão. Quando uma conexão é interrompida, SQL Serverpara todos os comandos em execução no momento e reverte a transação.
Quassnoi 17/11/2009
1
Portanto, a solução da DyingCactus parece que resolve o meu problema, obrigado pela ajuda.
jonathanpeppers
Se você precisar abortar com algum erro, sim, essa é a melhor opção.
Quassnoi