Хобрук: Ваш путь к мастерству в программировании

Ошибки не обрабатываются блоком Try / Catch в SQL Server

Мы используем следующий шаблон обработки ошибок в хранимых процедурах SQL Server:

ALTER PROCEDURE [dbo].[USP_Districtdata_Import]
    @DistrictData DistrictData Readonly
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
    BEGIN TRAN

    --Insert the new records into BudgetDistrict Table.
    INSERT INTO [dbo].[BudgetDistrict]
    (
     DistrictID,
     [Year],
     Season,
     BudgetStateID,
     ProjectedReturnCountIsCalc,
     RowStatus,
     CreatedBy,
     CreatedDate,
     LastModifiedBy,
     LastModifiedDate,
     EnableBudgetLock
     )

    SELECT
    DISTINCT list.[District Id],list.[Year],list.[Season],1,0,'A',@CreatedBy,@Updtime,@CreatedBy,@Updtime,0
    FROM @DistrictData liston]
        AND bud.RowStatus = 'A'
        )
    LEFT OUTER JOIN [dbo].[BudgetDistrict] bud
      ON (bud.DistrictID  = list.[District Id]
        AND bud.[Year]    = list.[Year]
        AND bud.[Season]  = list.[Seas
    WHERE bud.DistrictID IS NULL


    --Update the existing pending budgets
    UPDATE wk
    SET  wk.Budget         = list.[Budget], 
         wk.BudgetAdjusted = list.[Budget],
         wk.ProjectedReturnCount = list.[ProjectedReturn Count], 
         wk.CreatedBy      = @CreatedBy, 
         wk.CreatedDate    = @Updtime,
         wk.LastModifiedBy = @CreatedBy, 
         wk.LastModifiedDate = @Updtime  
    FROM @DistrictData list
    INNER JOIN [dbo].[BudgetDistrict] bud
      ON (bud.DistrictID  = list.[District Id]
        AND bud.[Year]    = list.[Year]
        AND bud.[Season]  = list.[Season])
    INNER JOIN [dbo].[BudgetDistrictWeekly] wk
      ON (wk.NationalBudgetID = bud.BudgetDistrictID
       AND wk.[WeekDate]      = list.[Week])
    WHERE bud.RowStatus  = 'A'
       AND wk.RowStatus  = 'A'
       AND bud.BudgetStateID = 1

    --Insert the new budgets
    INSERT INTO [dbo].[BudgetDistrictWeekly]
    (
    WeekDate,
    Budget,
    BudgetAdjusted,
    RowStatus,
    CreatedBy,
    CreatedDate,
    LastModifiedBy,
    LastModifiedDate,
    ProjectedReturnCount
    )

    SELECT LIST.[Week],list.[Budget],list.[Budget],'A',@CreatedBy,@Updtime,@CreatedBy,@Updtime,[ProjectedReturn Count]
    FROM @DistrictData list
    LEFT JOIN [dbo].[BudgetDistrict] bud
      ON (bud.DistrictID  = list.[District Id]
        AND bud.[Year]    = list.[year]
        AND bud.[Season]  = list.Season
        AND bud.RowStatus = 'A')
    WHERE bud.DistrictID IS NULL

       IF @@ERROR = 0
       BEGIN
              COMMIT TRAN;
      END
    END TRY
    BEGIN CATCH
        SELECT 
            ERROR_NUMBER() AS ErrorNumber,  
            ERROR_LINE() AS ErrorLine, 
            ERROR_MESSAGE() AS ErrorMessage;
        ROLLBACK TRAN;
    END CATCH

SET NOCOUNT OFF;
END

но когда в хранимой процедуре возникает указанная ниже ошибка, блок try / catch не работает.

Сведения об ошибке: хранимая процедура попыталась вставить значение NULL в ненулевой столбец.

Во время выполнения хранимой процедуры я получил следующую ошибку

Счетчик транзакций после EXECUTE указывает на несовпадение количества операторов BEGIN и COMMIT. Предыдущий счетчик = 1, текущий счетчик = 0.

Msg 3903, уровень 16, состояние 1, строка 30
Запрос ROLLBACK TRANSACTION не имеет соответствующей BEGIN TRANSACTION.

Почему исключение не обрабатывается? Пожалуйста помоги


  • Не ответ, но IF @@ERROR = 0 избыточен. если сервер Sql вызывает ошибку, поток должен перейти непосредственно к блоку catch. 11.05.2016
  • означает, что нам не нужно использовать @@ ERROR = 0 в блоке try / catch? 11.05.2016
  • да. это именно то, что это значит. 11.05.2016
  • Возможно, где-то в ваших утверждениях есть внутренняя транзакция? вы можете показать код, который создает эту проблему? 11.05.2016
  • никаких других внутренних транзакций. есть два оператора вставки и один оператор обновления. я отредактирую свой вопрос 11.05.2016
  • Некоторые ошибки не обнаруживаются оператором CATCH, например синтаксические ошибки, например. Дополнительные сведения см. В документации (Ошибки, на которые не влияет TRY… CATCH Construct). 11.05.2016
  • я читаю это. но, согласно документации, ошибки с уровнем серьезности 20 и выше не обрабатываются. но эта ошибка имеет только степень серьезности 16 11.05.2016
  • Все еще жду редактирования вашего вопроса ... 11.05.2016
  • добавлен код, пожалуйста, проверьте 11.05.2016
  • try catch предназначен для обработки ошибок выполнения, а не синтаксических ошибок 23.07.2020

Ответы:


1

Согласно документации (Ошибки, на которые не влияет конструкция TRY… CATCH ) оператор CATCH не обнаруживает некоторые ошибки.

Особенно:

  • Ошибки компиляции, такие как синтаксические ошибки, препятствующие запуску пакета.
  • Ошибки, возникающие во время перекомпиляции на уровне операторов, например ошибки разрешения имен объектов, возникающие после компиляции из-за отложенного разрешения имен.

Довольно типичная ситуация, когда хранимая процедура обращается к столбцу таблицы (или обращается к объекту), которые были удалены после создания хранимой процедуры.

См. Образец ниже:

create table #test (id int, somecolumn uniqueidentifier)
GO
create procedure #testSP
as
begin try
    set nocount on;

    begin tran;

    insert into #test (id, somecolumn)
    values (1, 0x);

    commit;

end try
begin catch
    rollback;
    print 'Error happened';
end catch
GO
exec #testSP
GO
alter table #test
    drop column somecolumn
GO
exec #testSP
GO

Приведенный выше код производит

Сообщение 207, уровень 16, состояние 1, процедура #testSP _..._ 00053EAF, строка 33 Недопустимое имя столбца 'somecolumn'.

Сообщение 266, уровень 16, состояние 2, процедура #testSP _..._ 00053EAF, строка 33 Счетчик транзакций после EXECUTE указывает на несовпадающее количество операторов BEGIN и COMMIT. Предыдущий счетчик = 1, текущий счетчик = 2.

Как видите, несмотря на уровень серьезности 16, ошибки не обнаруживаются и сообщение Error happened не печатается.

11.05.2016
  • Спасибо за объяснение. Значит, вы имеете в виду, что Вставить нулевое значение в ненулевой столбец также подпадают под эту категорию? 11.05.2016
  • @bmsqldev, нет. Но ERROR_MESSAGE() возвращает текст сообщения об ошибке, из-за которой был запущен блок CATCH конструкции TRY… CATCH. Возможно, что-то произошло ранее в стеке вызовов. . 11.05.2016
  • возможно. но мне интересно, почему это тоже отображается как настоящая ошибка. sproc должен выдать номер ошибки и сообщение об ошибке, если вышеуказанный блок try / catch работает 11.05.2016
  • @bmsqldev, попробуйте воспроизвести ситуацию, запустив свой код из сохраненной процедуры (и, вероятно, из try-catch), выполняя одни и те же операторы один за другим, я думаю, это должно помочь прояснить, что на самом деле происходит, и найти суть , где что-то идет не так. 11.05.2016
  • когда я выполнил код из блока sproc try / catch, ошибка правильно улавливается. Я использовал ту же структуру с операторами adhoc sql. 11.05.2016
  • Я выяснил способ обработки согласованности данных в этой ситуации по этой ссылке mssqltips.com/sqlservertip/4018/ 11.05.2016
  • @bmsqldev, хорошо, если вы нашли решение, однако для меня все еще не ясно, что происходит. Может быть catch действительно работает правильно в вашей ситуации, но транзакция уже откатывается, когда достигается catch блок? Затем в блоке catch снова вызывается rollback, вызывая несоответствие количества транзакций. Не похоже, что вставка значения NULL в ненулевой столбец автоматически вызывает откат. Может быть, какие-то триггеры на целевых таблицах работают rollback при определенных условиях? 11.05.2016
  • Я не уверен, происходит ли откат при достижении блока catch. это стандартный формат, которому мы следуем. пожалуйста, помогите мне, если есть какие-либо проблемы с синтаксисом try / catch, который я использую. Также я бы добавил еще одну вещь, на самом деле я вижу, что ошибка обнаруживается на вкладке набора результатов вместе с этими системными ошибками на вкладке сообщений. 12.05.2016
  • @bmsqldev, внутри catch вы делаете rollback безоговорочно. Если транзакция уже откатывается, это вызовет ошибку Запрос ROLLBACK TRANSACTION не имеет соответствующей ошибки BEGIN TRANSACTION.. Я думаю, что лучше использовать if @@trancount > 0 rollback (в предоставленной вами ссылке также используется этот шаблон). 12.05.2016
  • спасибо за предложение. Я наконец понял, почему появляются сообщения об ошибках. Я фактически выполняю хранимую процедуру в блоке транзакций begin tran rollback ..... что вызывает ошибку. когда я выполняю его за пределами блока, он правильно улавливает исключение 12.05.2016

  • 2

    есть способ, если вы можете использовать динамический sql, поэтому попробуйте catch работает

    24.03.2020
    Новые материалы

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..