Qual é o melhor método para adicionar tratamento de erros nos procs armazenados do SQL 2005?

11

Qual é uma boa maneira de tornar os processos armazenados robustos o suficiente para que eles possam ser dimensionados muito bem e também conter manipulação de erros?

Além disso, qual é a melhor maneira de lidar com vários cenários de erro em um processo armazenado e ter um sistema de feedback inteligente que retornará informações de erro significativas para os aplicativos que estão chamando?

kacalapy
fonte
2
Tente usar o bloco try catch mais recente do SQL Server 2005. sommarskog.se/error_handling_2005.html
Sankar Reddy
Oi @Kacalapy ~ Gostaria de recomendar no futuro que cada pergunta fosse feita por si só e, dessa forma, podemos ter respostas específicas focadas em uma pergunta por vez. Convido você a fazer isso com esta pergunta.
jcolebrand

Respostas:

12

Alex Kuznetsov tem um ótimo capítulo em seu livro Defensive Database Programming (Capítulo 8), que aborda as configurações T-SQL TRY ... CATCH, transações T-SQL e configurações SET XACT_ABORT, além de usar o tratamento de erros do lado do cliente. Isso o ajudará muito a decidir qual das opções faz mais sentido para o que você precisa realizar.

Ele está disponível para livre a este site . Não sou de forma alguma afiliada à empresa, mas possuo a versão impressa desse livro.

Há muitos pequenos detalhes sobre esse assunto que são explicados muito bem por Alex.

Por solicitação de Nick ... (mas nem tudo isso está no capítulo)

Em termos de escala, você precisa ser brutalmente honesto sobre quais atividades precisam estar no código db e quais devem estar no aplicativo. Você já reparou como o código de execução rápida tende a voltar ao design para uma única preocupação por método?

A maneira mais fácil de se comunicar seria códigos de erro personalizados (> 50.000). Também é bem rápido. Isso significa que você teria que manter o código db e o código do aplicativo sincronizados. Com um código de erro personalizado, você também pode retornar informações úteis na sequência de mensagens de erro. Como você possui um código de erro estritamente para essa situação, é possível gravar um analisador no código do aplicativo adaptado ao formato de dados do erro.

Além disso, quais condições de erro precisam repetir a lógica no banco de dados? Se você quiser tentar novamente depois de X segundos, é melhor lidar com isso no código do aplicativo para que a transação não bloqueie tanto. Se você apenas enviar novamente uma operação DML imediatamente, a repetição no SP poderá ser mais eficiente. Porém, lembre-se de que você precisará duplicar o código ou adicionar uma camada de SPs para realizar uma nova tentativa.

Realmente, atualmente é a maior dor com a lógica TRY ... CATCH no SQL Server no momento. Isso pode ser feito, mas é um pouco idiota. Procure algumas melhorias no SQL Server 2012, especialmente no lançamento de exceções do sistema (preservando o número do erro original). Além disso, há FORMATMESSAGE , que adiciona alguma flexibilidade na construção de mensagens de erro, especialmente para fins de log.

Phil Helmer
fonte
Ótimos conselhos e um livro muito bom!
Marian
O Red Gate oferece vários E-books gratuitos extremamente úteis, e este é certamente um dos melhores. Ótima sugestão.
Matt M
Nem todos os livros fazem isso, mas a versão gratuita do livro "Defensivo ..." de Kuznetsov não contém os dois últimos capítulos sobre Níveis de isolamento de transações e modificações de desenvolvimento que sobrevivem à simultaneidade. Para mim. o conteúdo lá valeu a compra.
23811 Phil Helmer
7

Este é o nosso modelo (registro de erros removido)

Notas:

  • Sem XACT_ABORT, todos os TXN iniciam e confirmam / recuperam devem ser emparelhados
  • Uma confirmação diminui @@ TRANCOUNT
  • Uma reversão retorna @@ TRANCOUNT para zero, para que você obtenha o erro 266
  • Você não pode ROLLBACK apenas a camada atual (por exemplo, decrementar @@ TRANCOUNT na reversão)
  • XACT_ABORT suprime o erro 266
  • Cada processo armazenado deve estar em conformidade com o mesmo modelo para que cada chamada seja atômica
  • A verificação de reversão é realmente redundante devido a XACT_ABORT. No entanto, isso me faz sentir melhor, parece estranho sem e permite situações em que você não quer
  • Isso permite TXNs do lado do cliente (como LINQ)
  • Remus Rusanu tem um shell semelhante que usa save points. Prefiro uma chamada de banco de dados atômica e não uso atualizações parciais como o artigo

... portanto, não crie mais TXNs do que você precisa

Contudo,

CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON

DECLARE @starttrancount int

BEGIN TRY
    SELECT @starttrancount = @@TRANCOUNT

    IF @starttrancount = 0
        BEGIN TRANSACTION

       [...Perform work, call nested procedures...]

    IF @starttrancount = 0 
        COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
        ROLLBACK TRANSACTION
    RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
gbn
fonte
e se @@ TRANCOUNT for maior que 0? você não faz nenhum trabalho ou tem algum feedback?
precisa saber é o seguinte
@kacalapy: não existe transação aninhada, portanto não iniciamos outro scribd.com/doc/49579859/33/Nested-Transactions-Are-Real
gbn
3

Uso Try / Catch, mas também colho o máximo de informações possível e as escrevo em um log de erros APÓS a reversão. Neste exemplo, "LogEvent" é um procedimento armazenado que grava em uma tabela EventLog, contendo os detalhes do que aconteceu. GetErrorInfo () é uma chamada de função que retorna a mensagem de erro exata.

Quando ocorre um erro, as informações são coletadas, o procedimento passa para a seção de tratamento de erros e emite uma reversão. As informações são gravadas no log e o procedimento é encerrado.

Considerando as chamadas extras de procedimento / função envolvidas, parece um pouco exagerado. NO ENTANTO, esse método é extremamente útil ao tentar depurar o problema.

exec LogEvent @Process, @Database, 'Tentativa de inserir blá blá blá'
COMEÇAR A TENTAR
  inserir no MyTable
  selecione Valores
    de MyOtherTable

  selecione @rowcount = @@ ROWCOUNT
FIM DA EXPERIÊNCIA
-- Manipulação de erros
COMEÇAR A CAPTURA
  selecione @error = ERROR_NUMBER (),
         @rowcount = -1,
         @TableAction = 'inserir',
         @TableName = @Database + '.MyTable',
         @AdditionalInfo = '(Tentativa de inserir blá blá blá)' + dbo.GetErrorInfo ()
   GOTO TableAccessError
FIM DA CAPTURA

.
.
.
.

TableAccessError:
SE (@@ TRANCOUNT> 0) ROLLBACK
selecione @ saída = superior (@TableAction) + 
       'ERRO - Ocorreu um erro enquanto' 
       case (@TableAction)
         quando 'atualizar' e 'atualizar'
         quando 'excluir' e 'excluir'
         else @TableAction + 'ing'
       end + 
       'records' + 
       case (@TableAction) 
         quando 'selecionar' e 'de' 
         quando 'update' then 'in' 
         quando 'inserir' e 'em'
         else 'from'   
         end + 
         'the' + @TableName + 'table.'
selecione @ saída = @ saída + '@@ ERRO:' + conversão (varchar (8), @ erro) 
selecione @ saída = @ saída + '@@ ROWCOUNT:' + conversão (varchar (8), @ número de linhas) 

selecione @output = @output + isnull (@AdditionalInfo, '')
exec LogEvent @Process, @Database, @Output
RAISERROR (@ saída, 16,1) com log
selecione @ReturnCode = -1
GOTO THE_EXIT


datagod
fonte