Sql try catch: TRY…CATCH (Transact-SQL) — SQL Server

TRY…CATCH (Transact-SQL) — SQL Server


  • Статья

  • Чтение занимает 9 мин

Применимо к: SQL Server Azure SQL DatabaseУправляемый экземпляр SQL AzureAzure Synapse Analytics AnalyticsPlatform System (PDW)

Реализация обработчика ошибок на языке Transact-SQL похожа на обработку исключений в языках Microsoft Visual C# и Microsoft Visual C++. Группа инструкций на языке Transact-SQL может быть заключена в блок TRY. Если в блоке TRY возникает ошибка, элемент управления обычно передается другой группе инструкций, заключенной в блок CATCH.

Соглашения о синтаксисе Transact-SQL

Синтаксис

BEGIN TRY  
     { sql_statement | statement_block }  
END TRY  
BEGIN CATCH  
     [ { sql_statement | statement_block } ]  
END CATCH  
[ ; ]  

Примечание

Ссылки на описание синтаксиса Transact-SQL для SQL Server 2014 и более ранних версий, см. в статье Документация по предыдущим версиям.

Аргументы

sql_statement
Любая инструкция Transact-SQL.

statement_block
Любая группа инструкций языка Transact-SQL в пакете или заключенная в блок BEGIN…END.

Конструкция TRY…CATCH перехватывает все ошибки исполнения с кодом серьезности, большим чем 10, которые не закрывают подключение к базе данных.

За блоком TRY сразу же должен следовать блок CATCH. Размещение каких-либо инструкций между инструкциями END TRY и BEGIN CATCH вызовет синтаксическую ошибку.

Конструкция TRY…CATCH не может охватывать несколько пакетов. Конструкция TRY…CATCH не может охватывать множество блоков инструкций на языке Transact-SQL. Например: конструктор TRY…CATCH не может охватывать два блока BEGIN…END из инструкций на языке Transact-SQL и не может охватывать конструкцию IF…ELSE.

Если ошибки в блоке TRY не возникают, то после выполнения последней инструкции в блоке TRY управление передается инструкции, расположенной сразу после инструкции END CATCH.

Если же в коде, заключенном в блоке TRY, происходит ошибка, управление передается первой инструкции в соответствующем блоке CATCH. Когда код в блоке CATCH завершен, управление передается инструкции, стоящей сразу после инструкции END CATCH.

Примечание

Если инструкция END CATCH является последней инструкцией хранимой процедуры или триггера, управление передается обратно инструкции, вызвавшей эту хранимую процедуру или триггер.

Ошибки, обнаруженные в блоке CATCH, не передаются в вызывающее приложение. Если какие-либо сведения об ошибке должны быть возвращены в приложение, код в блоке CATCH должен выполнить передачу этой ошибки, используя любые доступные механизмы, такие как результирующие наборы инструкции SELECT либо инструкции RAISERROR и PRINT.

Конструкция TRY…CATCH может быть вложенной. Либо блок TRY, либо блок CATCH могут содержать вложенные конструкции TRY…CATCH. Например: блок CATCH может содержать внутри себя внедренную TRY…CATCH для управления ошибками, возникающими в коде CATCH.

Ошибки, обнаруженные в блоке CATCH, обрабатываются так же, как и ошибки, возникшие в любом другом месте. Если блок CATCH содержит внутри себя конструкцию TRY…CATCH, то любая ошибка во вложенном блоке TRY передаст управление во вложенный блок CATCH. Если нет вложенной конструкции TRY…CATCH, то ошибка передается обратно в то место, откуда этот блок с ошибкой был вызван.

Конструкции TRY…CATCH ловят неуправляемые ошибки из хранимых процедур или триггеров, исполняемых кодом в блоке TRY. Дополнительно хранимые процедуры или триггеры могут содержать свои собственные конструкции TRY…CATCH для обработки ошибок, возникающих в их коде. Например, когда блок TRY выполняет хранимую процедуру и в хранимой процедуре возникла ошибка, то ошибка может быть обработана следующими способами:

  • если хранимая процедура не содержит своей собственной конструкции TRY…CATCH, то ошибка передаст управление в блок CATCH, связанный с блоком TRY, содержащим инструкцию EXECUTE;

  • если хранимая процедура содержит конструкцию TRY. ..CATCH, то ошибка передаст управление в блок CATCH в хранимой процедуре. Когда блок CATCH завершится, управление перейдет к инструкции, стоящей сразу после инструкции EXECUTE, вызвавшей эту хранимую процедуру.

Инструкция GOTO не может быть использована для входа в блоки TRY или CATCH. Оператор GOTO может быть использован для перехода к метке внутри блока TRY или CATCH или для выхода из блоков TRY или CATCH.

Конструкция TRY…CATCH не может использоваться в пользовательских функциях.

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

  • функция ERROR_NUMBER() возвращает номер ошибки;

  • функция ERROR_SEVERITY() возвращает степень серьезности ошибки;

  • функция ERROR_STATE() возвращает код состояния ошибки;

  • функция ERROR_PROCEDURE() возвращает имя хранимой процедуры или триггера, в котором произошла ошибка;

  • функция ERROR_LINE() возвращает номер строки, которая вызвала ошибку, внутри подпрограммы;

  • функция ERROR_MESSAGE() возвращает полный текст сообщения об ошибке. Текст содержит значения подставляемых параметров, таких как длина, имена объектов или время.

Эти функции возвращают значение NULL, если их вызов происходит вне области блока CATCH. С помощью этих функций сведения об ошибке могут быть получены из любого места внутри блока CATCH. Например, следующий скрипт демонстрирует хранимую процедуру, которая содержит функции обработки ошибок. В блоке CATCH конструкции TRY...CATCH вызывается хранимая процедура и возвращаются сведения об ошибке.

-- Verify that the stored procedure does not already exist.  
IF OBJECT_ID ( 'usp_GetErrorInfo', 'P' ) IS NOT NULL   
    DROP PROCEDURE usp_GetErrorInfo;  
GO  
  
-- Create procedure to retrieve error information.  
CREATE PROCEDURE usp_GetErrorInfo  
AS  
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;  
GO  
  
BEGIN TRY  
    -- Generate divide-by-zero error.   
    SELECT 1/0;  
END TRY  
BEGIN CATCH  
    -- Execute error retrieval routine.  
    EXECUTE usp_GetErrorInfo;  
END CATCH;   

Функции ERROR_* также работают в блоке CATCH внутри хранимой процедуры, скомпилированной в собственном коде.

Ошибки, не обрабатываемые конструкцией TRY…CATCH

Конструкции TRY…CATCH не обрабатывают следующие условия.

  • Предупреждения и информационные сообщения с уровнем серьезности 10 или ниже.

  • Ошибки с уровнем серьезности 20 или выше, которые приводят к завершению обработки задачи компонентом Компонент SQL Server Database Engine для сеанса. Если возникла ошибка с уровнем серьезности 20 или выше, а подключение к базе данных не разорвано, конструкция TRY…CATCH обработает эту ошибку.

  • Такие запросы, как прерывания от клиента или разрыв соединения, вызванный с клиента.

  • Завершение сеанса системным администратором с помощью инструкции KILL.

Следующие типы ошибок не обрабатываются блоком CATCH, если они возникают на том же самом уровне выполнения, что и конструкция TRY. ..CATCH.

  • Ошибки компиляции, такие как ошибки синтаксиса, в результате которых пакет не будет выполнен.

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

  • Ошибки разрешения имен объектов

Эти ошибки возвращаются на уровень, на котором запускались пакеты, хранимые процедуры или триггеры.

Если ошибка возникает во время компиляции или перекомпиляции уровня инструкций на нижнем уровне исполнения (например, при выполнении процедуры sp_executesql или определенной пользователем хранимой процедуры) внутри блока TRY, эта ошибка возникнет на уровне, более низком, чем конструкция TRY…CATCH, и будет обрабатываться соответствующим блоком CATCH.

Следующий пример показывает, как ошибка разрешения имени объекта, формируемая инструкцией SELECT, не отлавливается конструкцией TRY. ..CATCH, но отлавливается блоком CATCH, когда та же самая инструкция SELECT выполняется внутри хранимой процедуры.

BEGIN TRY  
    -- Table does not exist; object name resolution  
    -- error not caught.  
    SELECT * FROM NonexistentTable;  
END TRY  
BEGIN CATCH  
    SELECT   
        ERROR_NUMBER() AS ErrorNumber  
       ,ERROR_MESSAGE() AS ErrorMessage;  
END CATCH  

Эта ошибка не отлавливается, а управление передается за пределы конструкции TRY...CATCH на уровень выше.

Выполнение инструкции SELECT внутри хранимой процедуры приведет к ошибке, которая возникнет на уровне ниже, чем блок TRY. Такая ошибка будет обработана конструкцией TRY...CATCH.

-- Verify that the stored procedure does not exist.  
IF OBJECT_ID ( N'usp_ExampleProc', N'P' ) IS NOT NULL   
    DROP PROCEDURE usp_ExampleProc;  
GO  
  
-- Create a stored procedure that will cause an   
-- object resolution error.   
CREATE PROCEDURE usp_ExampleProc  
AS  
    SELECT * FROM NonexistentTable;  
GO  
  
BEGIN TRY  
    EXECUTE usp_ExampleProc;  
END TRY  
BEGIN CATCH  
    SELECT   
        ERROR_NUMBER() AS ErrorNumber  
        ,ERROR_MESSAGE() AS ErrorMessage;  
END CATCH;  

Если ошибка, возникшая в блоке TRY, приведет к неправильному состоянию транзакции, то транзакция будет классифицироваться как нефиксированная транзакция. Ошибка, которая обычно останавливает выполнение транзакции за пределами блока TRY, приводит к тому, что транзакция входит в нефиксируемое состояние, когда ошибка возникает внутри блока TRY. Нефиксированные транзакции могут только выполнять операции чтения или ROLLBACK TRANSACTION. Транзакция не может выполнить инструкцию на языке Transact-SQL, которая будет выполнять операции записи для COMMIT TRANSACTION. Функция XACT_STATE возвращает значение -1, если транзакция была классифицирована как нефиксированная транзакция. Когда выполнение пакета заканчивается, компонентом Компонент Database Engine, будет выполнен откат любых активных нефиксируемых транзакций. Если при переходе транзакции в нефиксируемое состояние не было отправлено сообщение об ошибке, после завершения выполнения пакета сообщение об ошибке будет отправлено клиентскому приложению. Это указывает на то, что была обнаружена нефиксируемая транзакция и выполнен ее откат.

Дополнительные сведения о нефиксированных транзакциях и функции XACT_STATE см. в разделе XACT_STATE (Transact-SQL).

Примеры

A. Использование конструкции TRY…CATCH

В следующем примере приведена инструкция SELECT, вызывающая ошибку деления на нуль. Эта ошибка приводит к передаче управления связанному блоку CATCH.

BEGIN TRY  
    -- Generate a divide-by-zero error.  
    SELECT 1/0;  
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;  
END CATCH;  
GO  

Б.

Использование конструкции TRY…CATCH внутри транзакции

В следующем примере показано использование блока TRY...CATCH внутри транзакции. Инструкция внутри блока TRY приводит к ошибке нарушения ограничения.

BEGIN TRANSACTION;  
  
BEGIN TRY  
    -- Generate a constraint violation error.  
    DELETE FROM Production.Product  
    WHERE ProductID = 980;  
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  
        ROLLBACK TRANSACTION;  
END CATCH;  
  
IF @@TRANCOUNT > 0  
    COMMIT TRANSACTION;  
GO  

В. Использование TRY…CATCH с XACT_STATE

В следующем примере показано, как использовать конструкцию TRY...CATCH для обработки ошибок, возникших внутри транзакции. Функция XACT_STATE определяет, должна ли транзакция быть зафиксирована или откачена. В данном примере параметр SET XACT_ABORT находится в состоянии ON. В результате, если произойдет ошибка нарушения ограничения, транзакция станет нефиксируемой.

-- Check to see whether this stored procedure exists.  
IF OBJECT_ID (N'usp_GetErrorInfo', N'P') IS NOT NULL  
    DROP PROCEDURE usp_GetErrorInfo;  
GO  
  
-- Create procedure to retrieve error information.  
CREATE PROCEDURE usp_GetErrorInfo  
AS  
    SELECT   
         ERROR_NUMBER() AS ErrorNumber  
        ,ERROR_SEVERITY() AS ErrorSeverity  
        ,ERROR_STATE() AS ErrorState  
        ,ERROR_LINE () AS ErrorLine  
        ,ERROR_PROCEDURE() AS ErrorProcedure  
        ,ERROR_MESSAGE() AS ErrorMessage;  
GO  
  
-- SET XACT_ABORT ON will cause the transaction to be uncommittable  
-- when the constraint violation occurs.   
SET XACT_ABORT ON;  
  
BEGIN TRY  
    BEGIN TRANSACTION;  
        -- A FOREIGN KEY constraint exists on this table.  This   
        -- statement will generate a constraint violation error.  
        DELETE FROM Production.Product  
            WHERE ProductID = 980;  
  
    -- If the DELETE statement succeeds, commit the transaction.  
    COMMIT TRANSACTION;  
END TRY  
BEGIN CATCH  
    -- Execute error retrieval routine.  
    EXECUTE usp_GetErrorInfo;  
  
    -- Test XACT_STATE:  
        -- If 1, the transaction is committable.  
        -- If -1, the transaction is uncommittable and should   
        --     be rolled back.  
        -- XACT_STATE = 0 means that there is no transaction and  
        --     a commit or rollback operation would generate an error.  
  
    -- Test whether the transaction is uncommittable.  
    IF (XACT_STATE()) = -1  
    BEGIN  
        PRINT  
            N'The transaction is in an uncommittable state.' +  
            'Rolling back transaction.'  
        ROLLBACK TRANSACTION;  
    END;  
  
    -- Test whether the transaction is committable.
    -- You may want to commit a transaction in a catch block if you want to commit changes to statements that ran prior to the error. 
    IF (XACT_STATE()) = 1  
    BEGIN  
        PRINT  
            N'The transaction is committable.' +  
            'Committing transaction.'  
        COMMIT TRANSACTION;     
    END;  
END CATCH;  
GO  

Г. Использование конструкции TRY…CATCH

В следующем примере приведена инструкция SELECT, вызывающая ошибку деления на нуль. Эта ошибка приводит к передаче управления связанному блоку CATCH.

BEGIN TRY  
    -- Generate a divide-by-zero error.  
    SELECT 1/0;  
END TRY  
BEGIN CATCH  
    SELECT  
        ERROR_NUMBER() AS ErrorNumber  
        ,ERROR_SEVERITY() AS ErrorSeverity  
        ,ERROR_STATE() AS ErrorState  
        ,ERROR_PROCEDURE() AS ErrorProcedure  
        ,ERROR_MESSAGE() AS ErrorMessage;  
END CATCH;  
GO  

См. также:

THROW (Transact-SQL)
Степени серьезности ошибок ядра СУБД
ERROR_LINE (Transact-SQL)
ERROR_MESSAGE (Transact-SQL)
ERROR_NUMBER (Transact-SQL)
ERROR_PROCEDURE (Transact-SQL)
ERROR_SEVERITY (Transact-SQL)
ERROR_STATE (Transact-SQL)
RAISERROR (Transact-SQL)
@@ERROR (Transact-SQL)
GOTO (Transact-SQL)
BEGIN. ..END (Transact-SQL)
XACT_STATE (Transact-SQL)
SET XACT_ABORT (Transact-SQL)

Обработка ошибок в языке T-SQL — конструкция TRY CATCH | Info-Comp.ru

В языке T-SQL, как и во многих других языках программирования, есть возможность отслеживать и перехватывать ошибки, сегодня мы с Вами рассмотрим конструкцию TRY CATCH, с помощью которой мы как раз и можем обрабатывать исключительные ситуации, т.е. непредвиденные ошибки.

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

Самый простой пример — это деление на ноль, как Вы знаете, делить на ноль нельзя, но эта цифра все-таки может возникнуть в операциях деления. Также существуют и другие ошибки, которые могут возникнуть в операциях над нестандартными, некорректными данными, хотя те же самые операции с обычными данными выполняются без каких-либо ошибок.

Поэтому в языке Transact-SQL существует специальная конструкция TRY…CATCH, она появилась в 2005 версии SQL сервера, и которая используется для обработки ошибок. Если кто знаком с другими языками программирования, то Вам эта конструкция скорей всего знакома, так как она используется во многих языках программирования.

Заметка! Для профессионального изучения языка T-SQL рекомендую посмотреть мои видеокурсы по T-SQL.

Содержание

  1. Конструкция TRY CATCH в T-SQL
  2. Важные моменты про конструкцию TRY CATCH в T-SQL
  3. Функции для получения сведений об ошибках
  4. Пример использования конструкции TRY…CATCH для обработки ошибок

TRY CATCH – это конструкция языка Transact-SQL для обработки ошибок. Все, что Вы хотите проверять на ошибки, т.е. код в котором могут возникнуть ошибки, Вы помещаете в блок TRY. Начало данного блока обозначается инструкцией BEGIN TRY, а окончание блока, соответственно, END TRY.

Все, что Вы хотите выполнять в случае появления ошибки, т.е. те инструкции, которые должны выполниться, если в блоке TRY возникла ошибка, Вы помещаете в блок CATCH, его начало обозначается BEGIN CATCH, а окончание END CATCH. Если никаких ошибок в блоке TRY не возникло, то блок CATCH пропускается и выполняются инструкции, следующие за ним. Если ошибки возникли, то выполняются инструкции в блоке CATCH, а после выполняются инструкции, следующие за данным блоком, иными словами, все инструкции, следующие за блоком CATCH, будут выполнены, если, конечно же, мы принудительно не завершили выполнение пакета в блоке CATCH.


Сам блок CATCH не передает никаких сведений об обнаруженных ошибках в вызывающее приложение, если это нужно, например, узнать номер или описание ошибки, то для этого Вы можете использовать инструкции SELECT, RAISERROR или PRINT в блоке CATCH.

Важные моменты про конструкцию TRY CATCH в T-SQL

  • Блок CATCH должен идти сразу же за блоком TRY, между этими блоками размещение инструкций не допускается;
  • TRY CATCH перехватывает все ошибки с кодом серьезности, большим 10, которые не закрывают соединения с базой данных;
  • В конструкции TRY…CATCH Вы можете использовать только один пакет и один блок SQL инструкций;
  • Конструкция TRY…CATCH может быть вложенной, например, в блоке TRY может быть еще одна конструкция TRY…CATCH, или в блоке CATCH Вы можете написать обработчик ошибок, на случай возникновения ошибок в самом блоке CATCH;
  • Оператор GOTO нельзя использовать для входа в блоки TRY или CATCH, он может быть использован только для перехода к меткам внутри блоков TRY или CATCH;
  • Обработка ошибок TRY…CATCH в пользовательских функциях не поддерживается;
  • Конструкция TRY…CATCH не обрабатывает следующие ошибки: предупреждения и информационные сообщения с уровнем серьезности 10 или ниже, разрыв соединения, вызванный клиентом, завершение сеанса администратором с помощью инструкции KILL.

Функции для получения сведений об ошибках

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

  • ERROR_NUMBER() – возвращает номер ошибки;
  • ERROR_MESSAGE() — возвращает описание ошибки;
  • ERROR_STATE() — возвращает код состояния ошибки;
  • ERROR_SEVERITY() — возвращает степень серьезности ошибки;
  • ERROR_PROCEDURE() — возвращает имя хранимой процедуры или триггера, в котором произошла ошибка;
  • ERROR_LINE() — возвращает номер строки инструкции, которая вызвала ошибку.

Если эти функции вызвать вне блока CATCH они вернут NULL.

Пример использования конструкции TRY…CATCH для обработки ошибок

Для демонстрации того, как работает конструкция TRY…CATCH, давайте напишем простую SQL инструкцию, в которой мы намеренно допустим ошибку, например, попытаемся выполнить операцию деление на ноль.

   
   --Начало блока обработки ошибок
   BEGIN TRY
        --Инструкции, в которых могут возникнуть ошибки
        DECLARE @TestVar1 INT = 10, 
                        @TestVar2 INT = 0, 
                        @Rez INT
        
        SET @Rez = @TestVar1 / @TestVar2

   END TRY
   --Начало блока CATCH
   BEGIN CATCH
        --Действия, которые будут выполняться в случае возникновения ошибки
        SELECT ERROR_NUMBER() AS [Номер ошибки],
                   ERROR_MESSAGE() AS [Описание ошибки]
        SET @Rez = 0       
   END CATCH

   SELECT @Rez AS [Результат]


В данном случае мы выводим номер и описание ошибки с помощью функций ERROR_NUMBER() и ERROR_MESSAGE(), а также присваиваем переменной с итоговым результатом значение 0, как видим, инструкции после блока CATCH продолжают выполняться.

У меня на этом все, надеюсь, материал был Вам полезен, пока!

TRY…CATCH (Transact-SQL) — SQL Server

  • Статья
  • 10 минут на чтение

Применяется к: SQL Server Azure SQL База данных Azure SQL Управляемый экземпляр Azure Synapse Analytics Analytics Platform System (PDW)

Реализует обработку ошибок для Transact-SQL, аналогичную обработке исключений в Microsoft Visual C# и Microsoft Visual C++ языки. Группа операторов Transact-SQL может быть заключена в блок TRY. Если в блоке TRY возникает ошибка, управление обычно передается другой группе операторов, заключенных в блоке CATCH.

Соглашения о синтаксисе Transact-SQL

Синтаксис

 BEGIN TRY
     { sql_statement | блок_операторов}
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
     [ { sql_statement | блок_операторов } ]
КОНЦЕВОЙ ЗАХВАТ
[ ; ]
 

Примечание

Чтобы просмотреть синтаксис Transact-SQL для SQL Server 2014 и более ранних версий, см. документацию по предыдущим версиям.

Аргументы

sql_statement
Любой оператор Transact-SQL.

блок_операторов
Любая группа операторов Transact-SQL в пакете или в блоке BEGIN…END.

Конструкция TRY…CATCH перехватывает все ошибки выполнения с серьезностью выше 10, которые не закрывают соединение с базой данных.

За блоком TRY должен сразу следовать связанный с ним блок CATCH. Включение любых других операторов между операторами END TRY и BEGIN CATCH приводит к синтаксической ошибке.

Конструкция TRY…CATCH не может охватывать несколько пакетов. Конструкция TRY…CATCH не может охватывать несколько блоков инструкций Transact-SQL. Например, конструкция TRY…CATCH не может охватывать два блока BEGIN…END инструкций Transact-SQL и не может охватывать конструкцию IF…ELSE.

Если в коде, заключенном в блоке TRY, нет ошибок, то после завершения выполнения последнего оператора в блоке TRY управление переходит к оператору, непосредственно следующему за соответствующим оператором END CATCH.

Если в коде, заключенном в блоке TRY, есть ошибка, управление переходит к первому оператору в соответствующем блоке CATCH. Когда код в блоке CATCH завершается, управление переходит к оператору сразу после оператора END CATCH.

Примечание

Если оператор END CATCH является последним оператором в хранимой процедуре или триггере, управление передается обратно оператору, вызвавшему хранимую процедуру или активировавшему триггер.

Ошибки, перехваченные блоком CATCH, не возвращаются вызывающему приложению. Если какая-либо часть информации об ошибке должна быть возвращена приложению, код в блоке CATCH должен сделать это с помощью таких механизмов, как наборы результатов SELECT или операторы RAISERROR и PRINT.

Конструкции TRY…CATCH могут быть вложенными. Либо блок TRY, либо блок CATCH могут содержать вложенные конструкции TRY…CATCH. Например, блок CATCH может содержать встроенную конструкцию TRY…CATCH для обработки ошибок, обнаруженных кодом CATCH.

Ошибки, обнаруженные в блоке CATCH, обрабатываются так же, как ошибки, сгенерированные где-либо еще. Если блок CATCH содержит вложенную конструкцию TRY…CATCH, любая ошибка во вложенном блоке TRY передаст управление вложенному блоку CATCH. Если нет вложенной конструкции TRY…CATCH, ошибка передается обратно вызывающей стороне.

Конструкции TRY…CATCH перехватывают необработанные ошибки хранимых процедур или триггеров, выполняемых кодом в блоке TRY. Кроме того, хранимые процедуры или триггеры могут содержать свои собственные конструкции TRY…CATCH для обработки ошибок, генерируемых их кодом. Например, когда блок TRY выполняет хранимую процедуру и в хранимой процедуре возникает ошибка, эту ошибку можно обработать следующими способами:0017

  • Если хранимая процедура не содержит собственной конструкции TRY…CATCH, ошибка возвращает управление блоку CATCH, связанному с блоком TRY, содержащим инструкцию EXECUTE.

  • Если хранимая процедура содержит конструкцию TRY. ..CATCH, ошибка передает управление блоку CATCH в хранимой процедуре. Когда код блока CATCH завершается, управление передается оператору сразу после оператора EXECUTE, вызвавшего хранимую процедуру.

Операторы GOTO нельзя использовать для входа в блок TRY или CATCH. Операторы GOTO можно использовать для перехода к метке внутри одного и того же блока TRY или CATCH или для выхода из блока TRY или CATCH.

Конструкция TRY…CATCH не может использоваться в пользовательской функции.

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

  • ERROR_NUMBER() возвращает номер ошибки.

  • ERROR_SEVERITY() возвращает серьезность.

  • ERROR_STATE() возвращает номер состояния ошибки.

  • ERROR_PROCEDURE() возвращает имя хранимой процедуры или триггера, в котором произошла ошибка.

  • ERROR_LINE() возвращает номер строки внутри подпрограммы, вызвавшей ошибку.

  • ERROR_MESSAGE() возвращает полный текст сообщения об ошибке. Текст включает значения, предоставленные для любых заменяемых параметров, таких как длины, имена объектов или время.

Эти функции возвращают NULL, если они вызываются за пределами блока CATCH. Информация об ошибках может быть получена с помощью этих функций из любого места в пределах области действия блока CATCH. Например, в следующем сценарии показана хранимая процедура, содержащая функции обработки ошибок. В блоке CATCH конструкции TRY...CATCH вызывается хранимая процедура и возвращается информация об ошибке.

 -- Убедитесь, что хранимая процедура еще не существует.
ЕСЛИ OBJECT_ID ('usp_GetErrorInfo', 'P') НЕ НУЛЬ
    ПРОЦЕДУРА УДАЛЕНИЯ usp_GetErrorInfo;
ИДТИ
  
-- Создать процедуру для получения информации об ошибке.
СОЗДАТЬ ПРОЦЕДУРУ usp_GetErrorInfo
КАК
ВЫБИРАТЬ
    ERROR_NUMBER() КАК ErrorNumber
    ,ERROR_SEVERITY() AS ErrorSeverity
    ,ERROR_STATE() КАК ErrorState
    ,ERROR_PROCEDURE() КАК ErrorProcedure
    ,ERROR_LINE() КАК ErrorLine
    ,ERROR_MESSAGE() AS ErrorMessage;
ИДТИ
  
НАЧАТЬ ПОПРОБУЙТЕ
    -- Генерировать ошибку деления на ноль. 
    ВЫБЕРИТЕ 1/0;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
    -- Выполнить процедуру поиска ошибок.
    ВЫПОЛНИТЬ usp_GetErrorInfo;
КОНЦЕВОЙ ЗАХВАТ;
 

Функции ERROR_* также работают в блоке CATCH внутри хранимой процедуры, скомпилированной в собственном коде.

Ошибки, не затронутые конструкцией TRY…CATCH

Конструкции TRY…CATCH не перехватывают следующие состояния:

  • Предупреждения или информационные сообщения с серьезностью 10 или ниже.

  • Ошибки с серьезностью 20 или выше, которые останавливают обработку задачи SQL Server Database Engine для сеанса. Если возникает ошибка серьезности 20 или выше, а соединение с базой данных не нарушено, TRY…CATCH обработает ошибку.

  • Предупреждения, такие как запросы прерывания клиента или разорванные клиентские соединения.

  • Когда системный администратор завершает сеанс с помощью оператора KILL.

Следующие типы ошибок не обрабатываются блоком CATCH, если они происходят на том же уровне выполнения, что и конструкция TRY. ..CATCH:

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

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

  • Ошибки разрешения имени объекта

Эти ошибки возвращаются на уровень, на котором был запущен пакет, хранимая процедура или триггер.

Если ошибка возникает во время компиляции или перекомпиляции на уровне операторов на более низком уровне выполнения (например, при выполнении sp_executesql или пользовательской хранимой процедуры) внутри блока TRY, ошибка возникает на более низком уровне, чем TRY.. .CATCH и будет обрабатываться соответствующим блоком CATCH.

В следующем примере показано, как ошибка разрешения имени объекта, сгенерированная оператором SELECT , не перехватывается конструкцией TRY...CATCH , но перехватывается блоком CATCH , когда тот же оператор SELECT выполняется внутри хранимой процедуры.

 НАЧАТЬ ПОПЫТКУ
    -- Таблица не существует; разрешение имени объекта
    -- ошибка не обнаружена.
    ВЫБЕРИТЕ * ИЗ несуществующей таблицы;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
    ВЫБИРАТЬ
        ERROR_NUMBER() КАК ErrorNumber
       ,ERROR_MESSAGE() AS ErrorMessage;
КОНЦЕВОЙ ЗАХВАТ
 

Ошибка не обнаружена, и управление передается из конструкции TRY...CATCH на следующий более высокий уровень.

Выполнение инструкции SELECT внутри хранимой процедуры приведет к возникновению ошибки на уровне ниже, чем блок TRY . Ошибка будет обработана конструкцией TRY...CATCH .

 -- Убедитесь, что хранимая процедура не существует.
ЕСЛИ OBJECT_ID ( N'usp_ExampleProc', N'P' ) НЕ НУЛЬ
    ПРОЦЕДУРА УДАЛЕНИЯ usp_ExampleProc;
ИДТИ
  
-- Создайте хранимую процедуру, которая вызовет
-- ошибка разрешения объекта.
СОЗДАТЬ ПРОЦЕДУРУ usp_ExampleProc
КАК
    ВЫБЕРИТЕ * ИЗ несуществующей таблицы;
ИДТИ
  
НАЧАТЬ ПОПРОБУЙТЕ
    ВЫПОЛНИТЬ usp_ExampleProc;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
    ВЫБИРАТЬ
        ERROR_NUMBER() КАК ErrorNumber
        ,ERROR_MESSAGE() AS ErrorMessage;
КОНЦЕВОЙ ЗАХВАТ;
 

Если ошибка, сгенерированная в блоке TRY, приводит к аннулированию состояния текущей транзакции, транзакция классифицируется как незафиксированная транзакция. Ошибка, которая обычно завершает транзакцию за пределами блока TRY, приводит к тому, что транзакция переходит в состояние невозможности фиксации, когда ошибка возникает внутри блока TRY. Нефиксируемая транзакция может выполнять только операции чтения или ROLLBACK TRANSACTION. Транзакция не может выполнять какие-либо инструкции Transact-SQL, которые могли бы создать операцию записи или COMMIT TRANSACTION. Функция XACT_STATE возвращает значение -1, если транзакция была классифицирована как незафиксированная транзакция. Когда пакет завершается, компонент Database Engine откатывает все активные незафиксированные транзакции. Если сообщение об ошибке не было отправлено, когда транзакция перешла в состояние невозможности фиксации, после завершения пакета в клиентское приложение будет отправлено сообщение об ошибке. Это указывает на обнаружение незафиксированной транзакции и ее откат.

Дополнительные сведения о незафиксированных транзакциях и функции XACT_STATE см. в разделе XACT_STATE (Transact-SQL).

Примеры

A. Использование TRY…CATCH

В следующем примере показана инструкция SELECT , которая вызовет ошибку деления на ноль. Ошибка приводит к переходу выполнения к соответствующему блоку CATCH .

 НАЧАТЬ ПОПЫТКУ
    -- Генерировать ошибку деления на ноль.
    ВЫБЕРИТЕ 1/0;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
    ВЫБИРАТЬ
        ERROR_NUMBER() КАК ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() КАК ErrorState
        ,ERROR_PROCEDURE() КАК ErrorProcedure
        ,ERROR_LINE() КАК ErrorLine
        ,ERROR_MESSAGE() AS ErrorMessage;
КОНЦЕВОЙ ЗАХВАТ;
ИДТИ
 

B. Использование TRY…CATCH в транзакции

В следующем примере показано, как блок TRY...CATCH работает внутри транзакции. Оператор внутри блока TRY генерирует ошибку нарушения ограничения.

 НАЧАТЬ ТРАНЗАКЦИЮ;
  
НАЧАТЬ ПОПРОБУЙТЕ
    -- Генерировать ошибку нарушения ограничения.
    УДАЛИТЬ ИЗ Production. Product
    ГДЕ ProductID = 980;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
    ВЫБИРАТЬ
        ERROR_NUMBER() КАК ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() КАК ErrorState
        ,ERROR_PROCEDURE() КАК ErrorProcedure
        ,ERROR_LINE() КАК ErrorLine
        ,ERROR_MESSAGE() AS ErrorMessage;
  
    ЕСЛИ @@TRANCOUNT > 0
        ОТКАТ СДЕЛКИ;
КОНЦЕВОЙ ЗАХВАТ;
  
ЕСЛИ @@TRANCOUNT > 0
    СОВЕРШИТЬ СДЕЛКУ;
ИДТИ
 

C. Использование TRY…CATCH с XACT_STATE

В следующем примере показано, как использовать конструкцию TRY...CATCH для обработки ошибок, возникающих внутри транзакции. Функция XACT_STATE определяет, должна ли транзакция быть зафиксирована или отменена. В этом примере SET XACT_ABORT равно ON . Это делает транзакцию незафиксированной при возникновении ошибки нарушения ограничения.

 -- Проверьте, существует ли эта хранимая процедура.
ЕСЛИ OBJECT_ID (N'usp_GetErrorInfo', N'P') НЕ НУЛЬ
    ПРОЦЕДУРА УДАЛЕНИЯ usp_GetErrorInfo;
ИДТИ
  
-- Создать процедуру для получения информации об ошибке. 
СОЗДАТЬ ПРОЦЕДУРУ usp_GetErrorInfo
КАК
    ВЫБИРАТЬ
         ERROR_NUMBER() КАК ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() КАК ErrorState
        ,ERROR_LINE () КАК ErrorLine
        ,ERROR_PROCEDURE() КАК ErrorProcedure
        ,ERROR_MESSAGE() AS ErrorMessage;
ИДТИ
  
-- SET XACT_ABORT ON приведет к невозможности фиксации транзакции
-- когда происходит нарушение ограничения.
УСТАНОВИТЬ XACT_ABORT ВКЛ.;
  
НАЧАТЬ ПОПРОБУЙТЕ
    НАЧАТЬ СДЕЛКУ;
        -- В этой таблице существует ограничение FOREIGN KEY. Этот
        -- оператор вызовет ошибку нарушения ограничения.
        УДАЛИТЬ ИЗ Production.Product
            ГДЕ ProductID = 980;
  
    -- Если инструкция DELETE выполнена успешно, зафиксируйте транзакцию.
    СОВЕРШИТЬ СДЕЛКУ;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
    -- Выполнить процедуру поиска ошибок.
    ВЫПОЛНИТЬ usp_GetErrorInfo;
  
    -- Проверка XACT_STATE:
        -- Если 1, транзакция фиксируется.
        -- Если -1, транзакция не поддается фиксации и должна
        -- откатиться назад. 
        -- XACT_STATE = 0 означает, что транзакции нет и
        -- операция фиксации или отката вызовет ошибку.
  
    -- Проверить, является ли транзакция незафиксированной.
    ЕСЛИ (XACT_STATE()) = -1
    НАЧИНАТЬ
        РАСПЕЧАТАТЬ
            N'Транзакция находится в неподдающемся фиксации состоянии.' +
            «Откат транзакции».
        ОТКАТ СДЕЛКИ;
    КОНЕЦ;
  
    -- Проверить, можно ли зафиксировать транзакцию.
    -- Вы можете захотеть зафиксировать транзакцию в блоке catch, если хотите зафиксировать изменения в операторах, которые выполнялись до возникновения ошибки.
    ЕСЛИ (XACT_STATE()) = 1
    НАЧИНАТЬ
        РАСПЕЧАТАТЬ
            N'Транзакция может быть зафиксирована.' +
            «Совершение транзакции».
        СОВЕРШИТЬ СДЕЛКУ;
    КОНЕЦ;
КОНЦЕВОЙ ЗАХВАТ;
ИДТИ
 

D. Использование TRY…CATCH

В следующем примере показана инструкция SELECT , которая вызовет ошибку деления на ноль. Ошибка приводит к переходу выполнения к соответствующему блоку CATCH .

 НАЧАТЬ ПОПЫТКУ
    -- Генерировать ошибку деления на ноль.
    ВЫБЕРИТЕ 1/0;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
    ВЫБИРАТЬ
        ERROR_NUMBER() КАК ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() КАК ErrorState
        ,ERROR_PROCEDURE() КАК ErrorProcedure
        ,ERROR_MESSAGE() AS ErrorMessage;
КОНЦЕВОЙ ЗАХВАТ;
ИДТИ
 

См. также

THROW (Transact-SQL)
Серьезность ошибок ядра базы данных
ERROR_LINE (Transact-SQL)
ERROR_MESSAGE (Transact-SQL)
ERROR_NUMBER (Transact-SQL)
ERROR_PROCEDURE (Transact-SQL ERR_SEOR_9003) SQL)
ERROR_STATE (Transact-SQL)
RAISERROR (Transact-SQL)
@@ERROR (Transact-SQL)
GOTO (Transact-SQL)
BEGIN…END (Transact-SQL)
XACT_STATE (Transact-SQL)
SET XACT_ABORT (Transact-SQL)

sql server — Как добавить Try/Catch в хранимую процедуру SQL

TRY / CATCH Обработка ошибок может происходить либо внутри, либо вне процедуры (или и там, и там). Примеры ниже демонстрируют обработку ошибок в обоих случаях.

Если вы хотите продолжить эксперимент, вы можете разветвить запрос в обозревателе данных Stack Exchange.

(здесь используется временная хранимая процедура… мы не можем создать обычных SP на SEDE, но функциональность такая же.)

 --наша хранимая процедура
создайте процедуру #myProc as --мы можем создавать #temporary хранимые процедуры только в SEDE.
  начинать
    НАЧАТЬ ПОПРОБУЙТЕ
      print 'Это наша хранимая процедура.'
      print 1/0 --<-- генерировать ошибку "Делить на ноль".
      print 'Мы не доберемся до этой линии.'
    КОНЕЦ ПОПЫТКИ
    
    НАЧАТЬ ЛОВИТЬ
      print 'Это блок CATCH в нашей хранимой процедуре:'
          + ' Строка ошибки #'+convert(varchar,ERROR_LINE())
          + ' процедуры '+ isnull(ERROR_PROCEDURE(),'(Main)')
      --print 1/0 --<-- генерировать еще одну ошибку "Делить на ноль".
        -- раскомментируйте строку выше, чтобы вызвать ошибку в CATCH ¹
    КОНЦЕВОЙ ЗАХВАТ
  конец
идти
--наш ГЛАВНЫЙ блок кода:
НАЧАТЬ ПОПРОБУЙТЕ
  print 'Это наша ОСНОВНАЯ процедура. '
  выполнить #myProc --выполнить хранимую процедуру
      --print 1/0 --<-- генерировать еще одну ошибку "Делить на ноль".
        -- раскомментируйте строку выше, чтобы вызвать ошибку в процедуре MAIN ²
  print 'Теперь наш ГЛАВНЫЙ блок кода sql продолжается.'
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
  print 'Это блок CATCH для нашего основного блока кода sql:'
          + ' Строка ошибки #'+convert(varchar,ERROR_LINE())
          + ' процедуры '+ isnull(ERROR_PROCEDURE(),'(Main)')
КОНЦЕВОЙ ЗАХВАТ
 

Вот результат выполнения приведенного выше sql как есть:

 Это наша ГЛАВНАЯ процедура.
Это наша хранимая процедура.
Это блок CATCH в нашей хранимой процедуре: строка ошибки № 5 процедуры #myProc
Теперь наш ГЛАВНЫЙ блок кода sql продолжается.
 

¹ Если раскомментировать «дополнительную строку ошибки» в блоке CATCH хранимой процедуры, получится:

 Это наша ГЛАВНАЯ процедура.
Это наша хранимая процедура.
Это блок CATCH в нашей хранимой процедуре: строка ошибки № 5 процедуры #myProc
Это блок CATCH для нашего блока кода MAIN sql: Строка ошибки № 13 процедуры #myProc
 

² Раскомментирование «дополнительной строки ошибки» из процедуры MAIN даст:

 Это наша ОСНОВНАЯ процедура. 
Это наша хранимая процедура.
Это блок CATCH в нашей хранимой процедуре: строка ошибки № 5 процедуры #myProc
Это блок CATCH для нашего основного блока кода sql: строка ошибки № 4 процедуры (основная)
 

Что касается хранимых процедур и обработки ошибок, может быть полезно (и более аккуратно) использовать одну динамическую хранимую процедуру для обработки ошибок для нескольких других процедур или разделов кода.

Вот пример:

 -- наша процедура обработки ошибок
создать процедуру #myErrorHandling как
  начинать
    print 'Ошибка #'+convert(varchar,ERROR_NUMBER())+': '+ERROR_MESSAGE()
    print ' произошло в строке #'+convert(varchar,ERROR_LINE())
         +' процедуры '+ isnull(ERROR_PROCEDURE(),'(Main)')
    если ERROR_PROCEDURE() имеет значение null --проверьте, была ли ошибка в основной процедуре
      print '*Выполнение не может быть продолжено после ошибки в процедуре MAIN.'
  конец
идти
создайте процедуру #myProc как --нашу тестовую хранимую процедуру
  начинать
    НАЧАТЬ ПОПРОБУЙТЕ
      print 'Это наша хранимая процедура. '
      напечатать 1/0 -- сгенерировать ошибку "Делить на ноль".
      print 'Мы не дойдем до этой линии.'
    КОНЕЦ ПОПЫТКИ
    НАЧАТЬ ЛОВИТЬ
     выполнить #myErrorHandling
    КОНЦЕВОЙ ЗАХВАТ
  конец
идти
НАЧНИТЕ ПОПРОБУЙТЕ -- наша ОСНОВНАЯ процедура
  print 'Это наша ОСНОВНАЯ процедура.'
  выполнить #myProc --выполнить хранимую процедуру
  print '*Ошибка остановила процедуру, но наш ГЛАВНЫЙ код может продолжить работу.'
  напечатать 1/0 -- сгенерировать еще одну ошибку «Делить на ноль».
  print 'Мы не дойдем до этой линии.'
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВИТЬ
  выполнить #myErrorHandling
КОНЦЕВОЙ ЗАХВАТ
 
 Это наша ГЛАВНАЯ процедура.
Это наша хранимая процедура.
 Ошибка № 8134: обнаружена ошибка деления на ноль.
 произошло в строке № 5 процедуры #myProc
*Ошибка остановила процедуру, но наш ГЛАВНЫЙ код может продолжить работу.
 Ошибка № 8134: обнаружена ошибка деления на ноль.
 произошло в строке № 5 процедуры (основной)
*Выполнение не может быть продолжено после ошибки в процедуре MAIN. 
 

В рамках блока TRY / CATCH следующие системные функции могут использоваться для получения информации об ошибке, вызвавшей CATCH блок для выполнения:

  • ERROR_NUMBER() возвращает номер ошибки.
  • ERROR_SEVERITY() возвращает серьезность.
  • ERROR_STATE() возвращает номер состояния ошибки.
  • ERROR_PROCEDURE() возвращает имя хранимой процедуры или триггера, в котором произошла ошибка.
  • ERROR_LINE() возвращает номер строки внутри подпрограммы, вызвавшей ошибку.
  • ERROR_MESSAGE() возвращает полный текст сообщения об ошибке. Текст включает значения, предоставленные для любых заменяемых параметров, таких как длины, имена объектов или время.

(Источник)

Обратите внимание, что существует два типа ошибок SQL: Terminal и Catchable .