Каков наилучший способ добавить обработку ошибок в хранимых процессах SQL 2005?

11

Какой хороший способ сделать хранимые прокы достаточно надежными, чтобы они могли очень хорошо масштабироваться и содержать обработку ошибок?

Кроме того, каков наилучший способ обработки нескольких сценариев ошибок в хранимом процессе и иметь интеллектуальную систему обратной связи, которая будет возвращать значимую информацию об ошибках вызывающим приложениям?

kacalapy
источник
2
Попробуйте использовать более новый блок TRY CATCH в SQL Server 2005. sommarskog.se/error_handling_2005.html
Санкар Редди,
Привет @ Kacalapy ~ Я хотел бы порекомендовать в будущем задавать каждый вопрос по-своему, и таким образом у нас могут быть конкретные ответы, сосредоточенные на одном вопросе за один раз. Я призываю вас сделать это с этим вопросом.
Jcolebrand

Ответы:

12

У Алекса Кузнецова есть большая глава в его книге « Защитное программирование базы данных» (глава 8), в которой рассматриваются T-SQL TRY ... CATCH, транзакции T-SQL и настройки SET XACT_ABORT, а также использование обработки ошибок на стороне клиента. Это поможет вам решить, какой из вариантов больше всего подходит для выполнения.

Он доступен бесплатно на этом сайте . Я никоим образом не связан с компанией, но у меня есть печатная версия этой книги.

Есть много маленьких деталей по этому вопросу, которые очень хорошо объяснил Алекс.

По просьбе Ника ... (но не все это в главе)

С точки зрения масштабирования, вы должны быть абсолютно честными в отношении того, какие действия должны быть в коде БД, а какие - в приложении. Вы когда-нибудь замечали, как быстро исполняемый код возвращается к разработке для одной задачи на метод?

Самый простой способ связи - это пользовательские коды ошибок (> 50 000). Это также довольно быстро. Это означает, что вам нужно синхронизировать код БД и приложения. С помощью специального кода ошибки вы также можете вернуть полезную информацию в строке сообщения об ошибке. Поскольку у вас есть код ошибки строго для этой ситуации, вы можете написать анализатор в коде приложения, адаптированном к формату данных ошибки.

Кроме того, при каких условиях ошибки требуется повторная логика в базе данных? Если вы хотите повторить попытку через X секунд, то лучше обработать это в коде приложения, чтобы транзакция не блокировалась так сильно. Если вы только повторно отправляете операцию DML, повторение ее в SP может быть более эффективным. Имейте в виду, однако, что вам придется дублировать код или добавить слой SP, чтобы выполнить повторную попытку.

Действительно, в настоящее время это самая большая проблема с логикой TRY ... CATCH в SQL Server на данный момент. Это можно сделать, но это немного глупо. Ищите некоторые улучшения в SQL Server 2012, особенно перезапуск системных исключений (с сохранением исходного номера ошибки). Кроме того, есть FORMATMESSAGE , который добавляет некоторую гибкость в создании сообщений об ошибках, особенно для целей ведения журнала.

Фил Хелмер
источник
Отличный совет и очень хорошая книга!
Marian
Red Gate предлагает несколько чрезвычайно полезных бесплатных электронных книг, и эта, безусловно, одна из лучших. Отличное предложение.
Мэтт М
Не все их книги делают это, но бесплатная версия книги Кузнецова "Оборонительный ..." не содержит последние 2 главы об уровнях изоляции транзакций и разработке модификаций, которые пережили параллелизм. Для меня. содержание там стоило покупки.
Фил Хелмер
7

Это наш шаблон (журнал ошибок удален)

Заметки:

  • Без XACT_ABORT все TXN начало и коммит / откат должны быть в паре
  • Декреты коммитов @@ TRANCOUNT
  • Откат возвращает @@ TRANCOUNT в ноль, чтобы вы получили ошибку 266
  • Вы не можете откатить только текущий слой (например, уменьшить @@ TRANCOUNT при откате)
  • XACT_ABORT подавляет ошибку 266
  • Каждый хранимый процесс должен соответствовать одному и тому же шаблону, поэтому каждый вызов является атомарным.
  • Проверка отката фактически избыточна из-за XACT_ABORT. Тем не менее, это заставляет меня чувствовать себя лучше, выглядит странно без, и позволяет в ситуациях, когда вы не хотите его
  • Это позволяет использовать TXN на стороне клиента (например, LINQ)
  • Remus Rusanu имеет похожую оболочку, которая использует точки сохранения. Я предпочитаю атомарный вызов БД и не использую частичные обновления, такие как их статья

... так что не создавайте больше TXN, чем вам нужно

Однако,

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
ГБН
источник
что если @@ TRANCOUNT больше 0? ты не работаешь или имеешь обратную связь?
Какалапы
@kacalapy: не существует такой вещи, как вложенная транзакция, поэтому мы не запускаем другую scribd.com/doc/49579859/33/Nested-Transactions-Are-Real
gbn
3

Я использую Try / Catch, но я также собираю как можно больше информации и записываю ее в журнал ошибок ПОСЛЕ отката. В этом примере «LogEvent» - это хранимая процедура, которая записывает данные в таблицу EventLog, содержащую сведения о том, что произошло. GetErrorInfo () - это вызов функции, который возвращает точное сообщение об ошибке.

При возникновении ошибки информация собирается, процедура переходит к разделу обработки ошибок и выполняет откат. Информация записывается в журнал, затем процедура завершается.

Принимая во внимание дополнительные вызовы процедур / функций, это кажется немного чрезмерным. ОДНАКО этот метод чрезвычайно полезен при отладке проблемы.

exec LogEvent @Process, @Database, 'Попытка вставить бла-бла-бла'
НАЧАТЬ
  вставить в MyTable
  выберите значения
    из MyOtherTable

  выберите @rowcount = @@ ROWCOUNT
КОНЕЦ ПОПРОБУЙТЕ
-- Обработка ошибок
НАЧАТЬ ЛОВУТЬ
  select @error = ERROR_NUMBER (),
         @rowcount = -1,
         @TableAction = 'insert',
         @TableName = @Database + '.MyTable',
         @AdditionalInfo = '(Попытка вставить бла-бла-бла)' + dbo.GetErrorInfo ()
   GOTO TableAccessError
КОНЕЦ ЛОВ

,
,
,
,

TableAccessError:
IF (@@ TRANCOUNT> 0) ROLLBACK
выберите @output = upper (@TableAction) + 
       «ОШИБКА - произошла ошибка при +» 
       case (@TableAction)
         когда «обновление», то «обновление»
         когда «удалить», то «удаление»
         еще @TableAction + 'ing'
       конец + 
       «записи» + 
       случай (@TableAction) 
         когда «выбрать», то «из» 
         когда «обновить», то «в» 
         когда «вставить», то «в»
         еще "от"   
         конец + 
         таблица '+ @TableName +'. '
select @output = @output + '@@ ERROR:' + convert (varchar (8), @ error) 
select @output = @output + '@@ ROWCOUNT:' + convert (varchar (8), @ rowcount) 

select @output = @output + isnull (@AdditionalInfo, '')
exec LogEvent @Process, @Database, @Output
RAISERROR (@ output, 16,1) с журналом
выберите @ReturnCode = -1
GOTO THE_EXIT


datagod
источник