Transação e Try-catch no trabalho do SQL Server

9

Temos operações DML em cada etapa de um trabalho do SQL Server. Para garantir a atualização / inserção será revertida no caso de algo der errado, eu enrolado as modificações de dados de cada etapa em TRY CATCHe TRANSACTIONblocos:

BEGIN TRY
    BEGIN TRANSACTION

        [[INSERT/update statements]] ...

    IF @@TRANCOUNT > 0
    BEGIN
        COMMIT TRANSACTION
        PRINT 'Successful.'
    END

END TRY

BEGIN CATCH
    SELECT
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage

    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRANSACTION
        PRINT 'Unsuccessful.'
    END
END CATCH

Isso garante que as manipulações de dados sejam revertidas em caso de erro (s)? Ou outras considerações devem ser levadas em consideração?

Seria melhor maneira de fazer isso (usando configurações, etc)?

Obrigado.

Céu
fonte

Respostas:

7

Prefiro recomendar um padrão como o de Tratamento de exceções e transações aninhadas :

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

Esse padrão verifica o XACT_STATE()bloco catch para se proteger contra transações não confirmadas :

Transações não confirmadas e XACT_STATE
Se um erro gerado em um bloco TRY fizer com que o estado da transação atual seja invalidado, a transação será classificada como uma transação não confirmada. Um erro que normalmente encerra uma transação fora de um bloco TRY faz com que uma transação entre em um estado não comprometível quando o erro ocorre dentro de um bloco TRY. Uma transação não confirmada pode executar apenas operações de leitura ou uma TRANSAÇÃO ROLLBACK. A transação não pode executar nenhuma instrução Transact-SQL que geraria uma operação de gravação ou uma COMMIT TRANSACTION. A função XACT_STATE retornará um valor de -1 se uma transação tiver sido classificada como uma transação não comprometida. Quando um lote é finalizado, o Mecanismo de Banco de Dados reverte todas as transações não confirmadas ativas. Se nenhuma mensagem de erro foi enviada quando a transação entrou em um estado não confirmado, quando o lote terminar, uma mensagem de erro será enviada ao aplicativo cliente. Isso indica que uma transação não confirmada foi detectada e revertida.

Seu código está procurando @@TRANCOUNTem locais onde não pode ser 0, ele usa uma mistura de mensagens informativas PRINT e conjuntos de resultados SELECT para se comunicar com sucesso, e não trata de erros recuperáveis. Idealmente, as exceções devem se propagar para o cliente, nesse caso, para o trabalho do agente (ou seja, sua captura deve aumentar novamente).

Remus Rusanu
fonte
Obrigado pela sua resposta útil e site fantástico! No entanto, estou me perguntando se ainda posso usar esse padrão com uma instrução DML simples (não um processo armazenado)? Também temos que salvar a transação como abaixo? (Não tenho um procedimento de armazenamento para usar): salve a transação usp_my_procedure_name;
Sky
2

O que você tem parece bom para mim. Eu sugeriria fazer algo com as informações, é claro, depois de você reverter a transação, por exemplo, gravá-la em um log.

datagod
fonte
11
Obrigado pela sua resposta, você poderia me dar uma dica de como gravá-la em um log?
Sky
3
Se você deseja gravar erros ou dados em uma tabela de log, antes de fazer a reversão, copie os dados que deseja para uma variável da tabela (é importante que você use uma variável da tabela, uma tabela temporária será revertida.) a reversão e insira os dados da variável da tabela na tabela de log.
HLGEM