Поделиться через


TRY...CATCH (Transact-SQL)

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

Реализует обработку ошибок для Transact-SQL, аналогичную обработке исключений на языках C# и 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
[ ; ]

Аргументы

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 TRY...CATCH блоке передает элемент управления в вложенный 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 выполнение блока:

Function Description
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.

Примеры

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

В. Используйте 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