TRY...CATCH (Transact-SQL)
Реализация обработчика ошибок на языке Transact-SQL похожа на обработку исключений в языках Microsoft Visual C# и Microsoft Visual C++. Группа инструкций на языке Transact-SQL может быть заключена в блок TRY. Если ошибка возникает в блоке TRY, управление передается следующей группе инструкций, заключенных в блок CATCH.
Синтаксис
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. Если инструкция END CATCH является последней инструкцией хранимой процедуры или триггера, управление передается обратно инструкции, вызвавшей эту хранимую процедуру или триггер.
Когда код в блоке CATCH завершен, управление передается инструкции, стоящей сразу после инструкции END CATCH. Ошибки, обнаруженные в блоке CATCH, не передаются в вызывающее приложение. Если какие-либо сведения об ошибке должны быть возвращены в приложение, код в блоке CATCH должен выполнить передачу этой ошибки, используя любые доступные механизмы, такие как результирующие наборы инструкции SELECT либо инструкции RAISERROR и PRINT. Дополнительные сведения об использовании инструкции RAISERROR в сочетании с конструкцией TRY…CATCH см. в разделе Использование конструкции TRY...CATCH в языке Transact-SQL.
Конструкция 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 вызывается хранимая процедура и возвращаются сведения об ошибке.
USE AdventureWorks2008R2;
GO
-- 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;
Ошибки, не обрабатываемые конструкцией TRY…CATCH
Конструкции TRY…CATCH не обрабатывают следующие условия.
Предупреждения и информационные сообщения с уровнем серьезности 10 или ниже.
Ошибки с уровнем серьезности 20 или выше, которые приводят к завершению обработки задачи компонентом SQL Server Database Engine для сеанса. Если возникла ошибка с уровнем серьезности 20 или выше, а соединение с базой данных не разорвано, конструкция TRY…CATCH обработает эту ошибку.
Такие запросы, как прерывания от клиента или разрыв соединения, вызванный с клиента.
Завершение сеанса системным администратором с помощью инструкции KILL.
Следующие типы ошибок обрабатываются блоком CATCH, если они возникают на том же самом уровне выполнения, что и конструкция TRY...CATCH.
Ошибки компиляции, такие как ошибки синтаксиса, в результате которых пакет не будет выполнен.
Ошибки, происходящие во время повторной компиляции уровня инструкций, такие как ошибки разрешения имен объектов, которые происходят после компиляции из-за отложенного разрешения имен.
Эти ошибки возвращаются на уровень, на котором запускались пакеты, хранимые процедуры или триггеры.
Если ошибка возникает во время компиляции или перекомпиляции уровня инструкций на нижнем уровне исполнения (например, при выполнении процедуры sp_executesql или определенной пользователем хранимой процедуры) внутри блока TRY, эта ошибка возникнет на уровне, более низком, чем конструкция TRY…CATCH, и будет обрабатываться соответствующим блоком CATCH. Дополнительные сведения см. в разделе Использование конструкции TRY...CATCH в языке Transact-SQL.
Следующий пример показывает, как ошибка разрешения имени объекта, формируемая инструкцией SELECT, не отлавливается конструкцией TRY…CATCH, но отлавливается блоком CATCH, когда та же самая инструкция SELECT выполняется внутри хранимой процедуры.
USE AdventureWorks2008R2;
GO
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, если транзакция была классифицирована как нефиксированная транзакция. Когда выполнение пакета заканчивается, компонентом Database Engine, будет выполнен откат любых активных нефиксируемых транзакций. Если при переходе транзакции в нефиксируемое состояние не было отправлено сообщение об ошибке, после завершения выполнения пакета сообщение об ошибке будет отправлено клиентскому приложению. Это указывает на то, что была обнаружена нефиксируемая транзакция и выполнен ее откат.
Дополнительные сведения о нефиксированных транзакциях и функциях XACT_STATE см. в разделах Использование конструкции TRY...CATCH в языке Transact-SQL и XACT_STATE (Transact-SQL).
Примеры
A. Использование TRY…CATCH
В следующем примере приведена инструкция SELECT, вызывающая ошибку деления на ноль. Эта ошибка приводит к передаче управления связанному блоку CATCH.
USE AdventureWorks2008R2;
GO
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 приводит к ошибке нарушения ограничения.
USE AdventureWorks2008R2;
GO
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. В результате, если произойдет ошибка нарушения ограничения, транзакция станет нефиксируемой.
USE AdventureWorks2008R2;
GO
-- 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.
IF (XACT_STATE()) = 1
BEGIN
PRINT
N'The transaction is committable.' +
'Committing transaction.'
COMMIT TRANSACTION;
END;
END CATCH;
GO
См. также