TRY...CATCH (Transact-SQL)

Применимо к:SQL Server База данных SQL Azure Управляемый экземпляр SQL Azure Управляемый экземпляр SQL Azure Конечная точка аналитики аналитики Synapse Analytics Analytics (PDW)SQL Analyticsв Microsoft FabricХранилище в Microsoft Fabric

Реализация обработчика ошибок на языке 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 (12.x) и более ранних версиях см . в документации по предыдущим версиям.

Аргументы

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 ядро СУБД для сеанса. Если возникла ошибка с уровнем серьезности 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;  

Нефиксируемые транзакции и XACT_STATE

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

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

Примеры

А. Использование конструкции 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  

B. Использование конструкции 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  

C. Использование 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  

См. также

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