Try begin sql: TRY…CATCH (Transact-SQL) — SQL Server
TRY…CATCH (Transact-SQL) — SQL Server
-
- Чтение занимает 8 мин
В этой статье
Применимо к:Applies to: SQL ServerSQL Server (все поддерживаемые версии) SQL ServerSQL Server (all supported versions) База данных SQL AzureAzure SQL DatabaseБаза данных SQL AzureAzure SQL Database Управляемый экземпляр SQL AzureAzure SQL Managed InstanceУправляемый экземпляр SQL AzureAzure SQL Managed Instance Azure Synapse AnalyticsAzure Synapse AnalyticsAzure Synapse AnalyticsAzure Synapse Analytics Параллельное хранилище данныхParallel Data WarehouseПараллельное хранилище данныхParallel Data WarehouseПрименимо к:Applies to: SQL ServerSQL Server (все поддерживаемые версии) SQL ServerSQL Server (all supported versions) База данных SQL AzureAzure SQL DatabaseБаза данных SQL AzureAzure SQL Database Управляемый экземпляр SQL AzureAzure SQL Managed InstanceУправляемый экземпляр SQL AzureAzure SQL Managed Instance Azure Synapse AnalyticsAzure Synapse AnalyticsAzure Synapse AnalyticsAzure Synapse Analytics Параллельное хранилище данныхParallel Data WarehouseПараллельное хранилище данныхParallel Data Warehouse
Реализация обработчика ошибок на языке Transact-SQLTransact-SQL похожа на обработку исключений в языках MicrosoftMicrosoft Visual C# и MicrosoftMicrosoft Visual C++.Implements error handling for Transact-SQLTransact-SQL that is similar to the exception handling in the MicrosoftMicrosoft Visual C# and MicrosoftMicrosoft Visual C++ languages. Группа инструкций на языке Transact-SQLTransact-SQL может быть заключена в блок TRY.A group of Transact-SQLTransact-SQL statements can be enclosed in a TRY block. Если ошибка возникает в блоке TRY, управление передается следующей группе инструкций, заключенных в блок CATCH.If an error occurs in the TRY block, control is passed to another group of statements that is enclosed in a CATCH block.
Синтаксические обозначения в Transact-SQLTransact-SQL Syntax Conventions
СинтаксисSyntax
BEGIN TRY
{ sql_statement | statement_block }
END TRY
BEGIN CATCH
[ { sql_statement | statement_block } ]
END CATCH
[ ; ]
АргументыArguments
sql_statementsql_statement
Любая из инструкций языка Transact-SQLTransact-SQL.Is any Transact-SQLTransact-SQL statement.
statement_blockstatement_block
Любая группа инструкций языка Transact-SQLTransact-SQL в пакете или заключенная в блок BEGIN…END.Any group of Transact-SQLTransact-SQL statements in a batch or enclosed in a BEGIN…END block.
RemarksRemarks
Конструкция TRY…CATCH перехватывает все ошибки исполнения с кодом серьезности, большим чем 10, которые не закрывают подключение к базе данных.A TRY…CATCH construct catches all execution errors that have a severity higher than 10 that do not close the database connection.
За блоком TRY сразу же должен следовать блок CATCH.A TRY block must be immediately followed by an associated CATCH block. Размещение каких-либо инструкций между инструкциями END TRY и BEGIN CATCH вызовет синтаксическую ошибку.Including any other statements between the END TRY and BEGIN CATCH statements generates a syntax error.
Конструкция TRY…CATCH не может охватывать несколько пакетов.A TRY…CATCH construct cannot span multiple batches. Конструкция TRY…CATCH не может охватывать множество блоков инструкций на языке Transact-SQLTransact-SQL.A TRY…CATCH construct cannot span multiple blocks of Transact-SQLTransact-SQL statements. Например: конструктор TRY…CATCH не может охватывать два блока BEGIN…END из инструкций на языке Transact-SQLTransact-SQL и не может охватывать конструкцию IF…ELSE.For example, a TRY…CATCH construct cannot span two BEGIN…END blocks of Transact-SQLTransact-SQL statements and cannot span an IF…ELSE construct.
Если ошибки в блоке TRY не возникают, то после выполнения последней инструкции в блоке TRY управление передается инструкции, расположенной сразу после инструкции END CATCH.If there are no errors in the code that is enclosed in a TRY block, when the last statement in the TRY block has finished running, control passes to the statement immediately after the associated END CATCH statement.
Если же в коде, заключенном в блоке TRY, происходит ошибка, управление передается первой инструкции в соответствующем блоке CATCH.If there is an error in the code that is enclosed in a TRY block, control passes to the first statement in the associated CATCH block. Когда код в блоке CATCH завершен, управление передается инструкции, стоящей сразу после инструкции END CATCH.When the code in the CATCH block finishes, control passes to the statement immediately after the END CATCH statement.
Примечание
Если инструкция END CATCH является последней инструкцией хранимой процедуры или триггера, управление передается обратно инструкции, вызвавшей эту хранимую процедуру или триггер.If the END CATCH statement is the last statement in a stored procedure or trigger, control is passed back to the statement that called the stored procedure or fired the trigger.
Ошибки, обнаруженные в блоке CATCH, не передаются в вызывающее приложение.Errors trapped by a CATCH block are not returned to the calling application. Если какие-либо сведения об ошибке должны быть возвращены в приложение, код в блоке CATCH должен выполнить передачу этой ошибки, используя любые доступные механизмы, такие как результирующие наборы инструкции SELECT либо инструкции RAISERROR и PRINT.If any part of the error information must be returned to the application, the code in the CATCH block must do so by using mechanisms such as SELECT result sets or the RAISERROR and PRINT statements.
Конструкция TRY…CATCH может быть вложенной.TRY…CATCH constructs can be nested. Либо блок TRY, либо блок CATCH могут содержать вложенные конструкции TRY…CATCH.Either a TRY block or a CATCH block can contain nested TRY…CATCH constructs. Например: блок CATCH может содержать внутри себя внедренную TRY…CATCH для управления ошибками, возникающими в коде CATCH.For example, a CATCH block can contain an embedded TRY…CATCH construct to handle errors encountered by the CATCH code.
Ошибки, обнаруженные в блоке CATCH, обрабатываются так же, как и ошибки, возникшие в любом другом месте.Errors encountered in a CATCH block are treated like errors generated anywhere else. Если блок CATCH содержит внутри себя конструкцию TRY…CATCH, то любая ошибка во вложенном блоке TRY передаст управление во вложенный блок CATCH.If the CATCH block contains a nested TRY…CATCH construct, any error in the nested TRY block will pass control to the nested CATCH block. Если нет вложенной конструкции TRY…CATCH, то ошибка передается обратно в то место, откуда этот блок с ошибкой был вызван.If there is no nested TRY…CATCH construct, the error is passed back to the caller.
Конструкции TRY…CATCH ловят неуправляемые ошибки из хранимых процедур или триггеров, исполняемых кодом в блоке TRY.TRY…CATCH constructs catch unhandled errors from stored procedures or triggers executed by the code in the TRY block. Дополнительно хранимые процедуры или триггеры могут содержать свои собственные конструкции TRY…CATCH для обработки ошибок, возникающих в их коде.Alternatively, the stored procedures or triggers can contain their own TRY…CATCH constructs to handle errors generated by their code. Например, когда блок TRY выполняет хранимую процедуру и в хранимой процедуре возникла ошибка, то ошибка может быть обработана следующими способами:For example, when a TRY block executes a stored procedure and an error occurs in the stored procedure, the error can be handled in the following ways:
если хранимая процедура не содержит своей собственной конструкции TRY…CATCH, то ошибка передаст управление в блок CATCH, связанный с блоком TRY, содержащим инструкцию EXECUTE;If the stored procedure does not contain its own TRY…CATCH construct, the error returns control to the CATCH block associated with the TRY block that contains the EXECUTE statement.
если хранимая процедура содержит конструкцию TRY…CATCH, то ошибка передаст управление в блок CATCH в хранимой процедуре.If the stored procedure contains a TRY…CATCH construct, the error transfers control to the CATCH block in the stored procedure. Когда блок CATCH завершится, управление перейдет к инструкции, стоящей сразу после инструкции EXECUTE, вызвавшей эту хранимую процедуру.When the CATCH block code finishes, control is passed back to the statement immediately after the EXECUTE statement that called the stored procedure.
Инструкция GOTO не может быть использована для входа в блоки TRY или CATCH.GOTO statements cannot be used to enter a TRY or CATCH block. Оператор GOTO может быть использован для перехода к метке внутри блока TRY или CATCH или для выхода из блоков TRY или CATCH.GOTO statements can be used to jump to a label inside the same TRY or CATCH block or to leave a TRY or CATCH block.
Конструкция TRY…CATCH не может использоваться в пользовательских функциях.The TRY…CATCH construct cannot be used in a user-defined function.
Получение информации об ошибкеRetrieving Error Information
В области блока CATCH для получения сведений об ошибке, приведшей к выполнению данного блока CATCH, можно использовать следующие системные функции:In the scope of a CATCH block, the following system functions can be used to obtain information about the error that caused the CATCH block to be executed:
функция ERROR_NUMBER() возвращает номер ошибки;ERROR_NUMBER() returns the number of the error.
функция ERROR_SEVERITY() возвращает степень серьезности ошибки;ERROR_SEVERITY() returns the severity.
функция ERROR_STATE() возвращает код состояния ошибки;ERROR_STATE() returns the error state number.
функция ERROR_PROCEDURE() возвращает имя хранимой процедуры или триггера, в котором произошла ошибка;ERROR_PROCEDURE() returns the name of the stored procedure or trigger where the error occurred.
функция ERROR_LINE() возвращает номер строки, которая вызвала ошибку, внутри подпрограммы;ERROR_LINE() returns the line number inside the routine that caused the error.
функция ERROR_MESSAGE() возвращает полный текст сообщения об ошибке.ERROR_MESSAGE() returns the complete text of the error message. Текст содержит значения подставляемых параметров, таких как длина, имена объектов или время.The text includes the values supplied for any substitutable parameters, such as lengths, object names, or times.
Эти функции возвращают значение NULL, если их вызов происходит вне области блока CATCH.These functions return NULL if they are called outside the scope of the CATCH block. С помощью этих функций сведения об ошибке могут быть получены из любого места внутри блока CATCH.Error information can be retrieved by using these functions from anywhere within the scope of the CATCH block. Например, следующий скрипт демонстрирует хранимую процедуру, которая содержит функции обработки ошибок.For example, the following script shows a stored procedure that contains error-handling functions. В блоке CATCH
конструкции TRY...CATCH
вызывается хранимая процедура и возвращаются сведения об ошибке.In the CATCH
block of a TRY...CATCH
construct, the stored procedure is called and information about the error is returned.
-- 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
внутри хранимой процедуры, скомпилированной в собственном коде.The ERROR_* functions also work in a CATCH
block inside a natively compiled stored procedure.
Ошибки, не обрабатываемые конструкцией TRY…CATCHErrors Unaffected by a TRY…CATCH Construct
Конструкции TRY…CATCH не обрабатывают следующие условия.TRY…CATCH constructs do not trap the following conditions:
Предупреждения и информационные сообщения с уровнем серьезности 10 или ниже.Warnings or informational messages that have a severity of 10 or lower.
Ошибки с уровнем серьезности 20 или выше, которые приводят к завершению обработки задачи компонентом Компонент SQL Server Database EngineSQL Server Database Engine для сеанса.Errors that have a severity of 20 or higher that stop the Компонент SQL Server Database EngineSQL Server Database Engine task processing for the session. Если возникла ошибка с уровнем серьезности 20 или выше, а подключение к базе данных не разорвано, конструкция TRY…CATCH обработает эту ошибку.If an error occurs that has severity of 20 or higher and the database connection is not disrupted, TRY…CATCH will handle the error.
Такие запросы, как прерывания от клиента или разрыв соединения, вызванный с клиента.Attentions, such as client-interrupt requests or broken client connections.
Завершение сеанса системным администратором с помощью инструкции KILL.When the session is ended by a system administrator by using the KILL statement.
Следующие типы ошибок не обрабатываются блоком CATCH, если они возникают на том же самом уровне выполнения, что и конструкция TRY…CATCH.The following types of errors are not handled by a CATCH block when they occur at the same level of execution as the TRY…CATCH construct:
Ошибки компиляции, такие как ошибки синтаксиса, в результате которых пакет не будет выполнен.Compile errors, such as syntax errors, that prevent a batch from running.
Ошибки, происходящие во время повторной компиляции уровня инструкций, такие как ошибки разрешения имен объектов, которые происходят после компиляции из-за отложенного разрешения имен.Errors that occur during statement-level recompilation, such as object name resolution errors that occur after compilation because of deferred name resolution.
Ошибки разрешения имен объектовObject name resolution errors
Эти ошибки возвращаются на уровень, на котором запускались пакеты, хранимые процедуры или триггеры.These errors are returned to the level that ran the batch, stored procedure, or trigger.
Если ошибка возникает во время компиляции или перекомпиляции уровня инструкций на нижнем уровне исполнения (например, при выполнении процедуры sp_executesql или определенной пользователем хранимой процедуры) внутри блока TRY, эта ошибка возникнет на уровне, более низком, чем конструкция TRY…CATCH, и будет обрабатываться соответствующим блоком CATCH.If an error occurs during compilation or statement-level recompilation at a lower execution level (for example, when executing sp_executesql or a user-defined stored procedure) inside the TRY block, the error occurs at a lower level than the TRY…CATCH construct and will be handled by the associated CATCH block.
Следующий пример показывает, как ошибка разрешения имени объекта, формируемая инструкцией SELECT
, не отлавливается конструкцией TRY...CATCH
, но отлавливается блоком CATCH
, когда та же самая инструкция SELECT
выполняется внутри хранимой процедуры.The following example shows how an object name resolution error generated by a SELECT
statement is not caught by the TRY...CATCH
construct, but is caught by the CATCH
block when the same SELECT
statement is executed inside a stored procedure.
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
на уровень выше.The error is not caught and control passes out of the TRY...CATCH
construct to the next higher level.
Выполнение инструкции SELECT
внутри хранимой процедуры приведет к ошибке, которая возникнет на уровне ниже, чем блок TRY
.Running the SELECT
statement inside a stored procedure will cause the error to occur at a level lower than the TRY
block. Такая ошибка будет обработана конструкцией TRY...CATCH
.The error will be handled by the TRY...CATCH
construct.
-- 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_STATEUncommittable Transactions and XACT_STATE
Если ошибка, возникшая в блоке TRY, приведет к неправильному состоянию транзакции, то транзакция будет классифицироваться как нефиксированная транзакция.If an error generated in a TRY block causes the state of the current transaction to be invalidated, the transaction is classified as an uncommittable transaction. Ошибка, которая обычно останавливает выполнение транзакции за пределами блока TRY, приводит к тому, что транзакция входит в нефиксируемое состояние, когда ошибка возникает внутри блока TRY.An error that ordinarily ends a transaction outside a TRY block causes a transaction to enter an uncommittable state when the error occurs inside a TRY block. Нефиксированные транзакции могут только выполнять операции чтения или ROLLBACK TRANSACTION.An uncommittable transaction can only perform read operations or a ROLLBACK TRANSACTION. Транзакция не может выполнить инструкцию на языке Transact-SQLTransact-SQL, которая будет выполнять операции записи для COMMIT TRANSACTION.The transaction cannot execute any Transact-SQLTransact-SQL statements that would generate a write operation or a COMMIT TRANSACTION. Функция XACT_STATE возвращает значение -1, если транзакция была классифицирована как нефиксированная транзакция.The XACT_STATE function returns a value of -1 if a transaction has been classified as an uncommittable transaction. Когда выполнение пакета заканчивается, компонентом Компонент Database EngineDatabase Engine, будет выполнен откат любых активных нефиксируемых транзакций.When a batch finishes, the Компонент Database EngineDatabase Engine rolls back any active uncommittable transactions. Если при переходе транзакции в нефиксируемое состояние не было отправлено сообщение об ошибке, после завершения выполнения пакета сообщение об ошибке будет отправлено клиентскому приложению.If no error message was sent when the transaction entered an uncommittable state, when the batch finishes, an error message will be sent to the client application. Это указывает на то, что была обнаружена нефиксируемая транзакция и выполнен ее откат.This indicates that an uncommittable transaction was detected and rolled back.
Дополнительные сведения о нефиксированных транзакциях и функции XACT_STATE см. в разделе XACT_STATE (Transact-SQL).For more information about uncommittable transactions and the XACT_STATE function, see XACT_STATE (Transact-SQL).
ПримерыExamples
A.A. Использование конструкции TRY…CATCHUsing TRY…CATCH
В следующем примере приведена инструкция SELECT
, вызывающая ошибку деления на нуль.The following example shows a SELECT
statement that will generate a divide-by-zero error. Эта ошибка приводит к передаче управления связанному блоку CATCH
.The error causes execution to jump to the associated CATCH
block.
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 внутри транзакцииUsing TRY…CATCH in a transaction
В следующем примере показано использование блока TRY...CATCH
внутри транзакции.The following example shows how a TRY...CATCH
block works inside a transaction. Инструкция внутри блока TRY
приводит к ошибке нарушения ограничения.The statement inside the TRY
block generates a constraint violation error.
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_STATEUsing TRY…CATCH with XACT_STATE
В следующем примере показано, как использовать конструкцию TRY...CATCH
для обработки ошибок, возникших внутри транзакции.The following example shows how to use the TRY...CATCH
construct to handle errors that occur inside a transaction. Функция XACT_STATE
определяет, должна ли транзакция быть зафиксирована или откачена.The XACT_STATE
function determines whether the transaction should be committed or rolled back. В данном примере параметр SET XACT_ABORT
находится в состоянии ON
.In this example, SET XACT_ABORT
is ON
. В результате, если произойдет ошибка нарушения ограничения, транзакция станет нефиксируемой.This makes the transaction uncommittable when the constraint violation error occurs.
-- 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
Примеры: Azure Synapse AnalyticsAzure Synapse Analytics и Параллельное хранилище данныхParallel Data WarehouseExamples: Azure Synapse AnalyticsAzure Synapse Analytics and Параллельное хранилище данныхParallel Data Warehouse
Г.D. Использование конструкции TRY…CATCHUsing TRY…CATCH
В следующем примере приведена инструкция SELECT
, вызывающая ошибку деления на нуль.The following example shows a SELECT
statement that will generate a divide-by-zero error. Эта ошибка приводит к передаче управления связанному блоку CATCH
.The error causes execution to jump to the associated CATCH
block.
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
См. также:See Also
THROW (Transact-SQL) THROW (Transact-SQL)
Степени серьезности ошибок ядра СУБД Database Engine Error Severities
ERROR_LINE (Transact-SQL) ERROR_LINE (Transact-SQL)
ERROR_MESSAGE (Transact-SQL) ERROR_MESSAGE (Transact-SQL)
ERROR_NUMBER (Transact-SQL) ERROR_NUMBER (Transact-SQL)
ERROR_PROCEDURE (Transact-SQL) ERROR_PROCEDURE (Transact-SQL)
ERROR_SEVERITY (Transact-SQL) ERROR_SEVERITY (Transact-SQL)
ERROR_STATE (Transact-SQL) ERROR_STATE (Transact-SQL)
RAISERROR (Transact-SQL) RAISERROR (Transact-SQL)
@@ERROR (Transact-SQL) @@ERROR (Transact-SQL)
GOTO (Transact-SQL) GOTO (Transact-SQL)
BEGIN…END (Transact-SQL) BEGIN…END (Transact-SQL)
XACT_STATE (Transact-SQL) XACT_STATE (Transact-SQL)
SET XACT_ABORT (Transact-SQL)SET XACT_ABORT (Transact-SQL)
SQL Server Try/Catch, Begin/Tran, RaiseError, Цикл И Возврат. Каков правильный порядок?
У меня есть следующая процедура. (Синтаксис не идеален, просто хочу дать вам, ребята, идею).
begin tran
begin try
while loop
some condition, if true
raiseerror('error', 16, 1)
end try
begin catch
if transaction > 0
rollback tran
select error message
end catch
if transaction > 0
commit tran
Вопросы:
Повысит силу ошибки в блоке catch? Если да, то будет ли транзакция полностью откатана (для всех итераций цикла)?
Я хочу, чтобы было #1, нужно ли мне добавлять «RETURN» после отката Трана?
Я задаю этот вопрос, потому что кажется, что цикл продолжается даже после некоторой ошибки в итерации.
sql
sql-server
raiseerror
Поделиться
Источник
RJ.
27 июля 2017 в 19:36
1 ответ
1
Взгляните на этот вопрос (заданный вашим покорным слугой): SQL Server XACT_ABORT с исключением
Хотя вопрос был немного другим, он показывает основную структуру того, как достичь того, что вы пытаетесь сделать.
30-секундное краткое изложение будет выглядеть следующим образом: используйте блоки try/catch, как вы это сделали, и в конце вашей процедуры есть раздел «failure», к которому будут направлены все уловы. Вы можете установить сообщение в каждом улове для ведения журнала или передать ошибку конечному пользователю.
Поделиться
Eli
27 июля 2017 в 20:48
Похожие вопросы:
TRY CATCH в SQL Server
Я использую SQL Server 2008. Я попытался выполнить следующее: BEGIN TRY SELECT 1/0; END TRY BEGIN CATCH PRINT ‘ERROR’ END CATCH; Но я получаю следующую ошибку: >Msg 170, Level 15, State 1, Line 1…
Имеет ли смысл использовать как @@error, так и try…catch обработку ошибок в sql server?
Имеет ли здесь вообще смысл строка @error? Будет ли он вообще поражен в сценарии ошибки? Что произойдет, если внутри блока try есть несколько операторов sql, за каждым из которых следует аналогичная…
TRY CATCH со связанным сервером в SQL Server 2005 не работает
Я пытаюсь поймать ошибку sql, возникшую при выполнении хранимой процедуры на связанном сервере. Оба сервера работают под управлением SQL Server 2005. Чтобы доказать эту проблему я создал хранимую…
Каков порядок исполнения в try, catch и finally
Если мы даем оператор return, как это попробовать, каким будет порядок оформления try{ — —- —— return a; } catch{ } finally{ } Вот каков будет порядок исполнения, если есть возврат в try….
Флеш печать заявления клиента (управление SQL Server студии)
Пожалуйста, смотрите код ниже: USE [Test] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [dbo].[Test] AS BEGIN begin tran begin try SET NOCOUNT ON; DECLARE @ID int DECLARE…
Предпочтительная форма для Try Catch, Begin Commit Rollback?
Какой путь лучше? BEGIN TRAN BEGIN TRY — Code here COMMIT TRAN END TRY BEGIN CATCH ROLLBACK TRAN END CATCH или BEGIN TRY BEGIN TRAN — Code here COMMIT TRAN END TRY BEGIN CATCH ROLLBACK TRAN END…
Sql Server — правильный синтаксис для отката транзакции
Приведенный ниже sql дает синтаксическую ошибку в RAISEERROR , которая исчезает, если я удаляю [Tran1] из запроса. Каков правильный синтаксис для отката транзакции с псевдонимами и последующего…
Удалить автоматически появляющиеся BEGIN TRAN и ROLLBACK в SQL Server 2008 R2
Я хочу удалить автоматическое появление BEGIN TRAN и ROLLBACK в SQL Server 2008 R2. Вот следующее изображение, которое я получаю при выборе нового запроса для записи.
SAP HANA-Begin tran, Rollback, commit
Я только что начал использовать SAP HANA. Я иду из среды SQL Server. Есть ли эквивалент BEGIN TRAN в SAP HANA? Похоже, есть команды Rollback и Commit , и я не могу понять, когда это работает, а…
SQL Server: BEGIN TRAN … COMMIT без ROLLBACK не откатывается в зависимости от ошибки
В Microsoft SQL Server я создаю тестовую таблицу с помощью CREATE TABLE [Test] ( [BookID] [int] NOT NULL, [Name] [varchar](512) NOT NULL, CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED ([BookID] ASC) )…
Как добавить Try/Catch в хранимую процедуру SQL
TRY
/ CATCH
обработка ошибок может происходить как внутри, так и вне процедуры (или и то, и другое). Приведенные ниже примеры демонстрируют обработку ошибок в обоих случаях.
Если вы хотите поэкспериментировать дальше, вы можете fork запрос на Stack Exchange Data Explorer .
(При этом используется временная хранимая процедура …, мы не можем создавать обычные SP на SEDE, но функциональность та же.)
--our Stored Procedure
create procedure #myProc as --we can only create #temporary stored procedures on SEDE.
begin
BEGIN TRY
print 'This is our Stored Procedure.'
print 1/0 --<-- generate a "Divide By Zero" error.
print 'We are not going to make it to this line.'
END TRY
BEGIN CATCH
print 'This is the CATCH block within our Stored Procedure:'
+ ' Error Line #'+convert(varchar,ERROR_LINE())
+ ' of procedure '+isnull(ERROR_PROCEDURE(),'(Main)')
--print 1/0 --<-- generate another "Divide By Zero" error.
-- uncomment the line above to cause error within the CATCH ¹
END CATCH
end
go
--our MAIN code block:
BEGIN TRY
print 'This is our MAIN Procedure.'
execute #myProc --execute the Stored Procedure
--print 1/0 --<-- generate another "Divide By Zero" error.
-- uncomment the line above to cause error within the MAIN Procedure ²
print 'Now our MAIN sql code block continues.'
END TRY
BEGIN CATCH
print 'This is the CATCH block for our MAIN sql code block:'
+ ' Error Line #'+convert(varchar,ERROR_LINE())
+ ' of procedure '+isnull(ERROR_PROCEDURE(),'(Main)')
END CATCH
Вот результат выполнения вышеупомянутого sql as-is:
This is our MAIN Procedure.
This is our Stored Procedure.
This is the CATCH block within our Stored Procedure: Error Line #5 of procedure #myProc
Now our MAIN sql code block continues.
1 Раскомментирование «additional error line» из блока CATCH хранимой процедуры приведет к:
This is our MAIN procedure.
This is our Stored Procedure.
This is the CATCH block within our Stored Procedure: Error Line #5 of procedure #myProc
This is the CATCH block for our MAIN sql code block: Error Line #13 of procedure #myProc
2 Раскомментирование «additional error line» из процедуры MAIN приведет к:
This is our MAIN Procedure.
This is our Stored Pprocedure.
This is the CATCH block within our Stored Procedure: Error Line #5 of procedure #myProc
This is the CATCH block for our MAIN sql code block: Error Line #4 of procedure (Main)
Что касается хранимых процедур и обработки ошибок, то может быть полезно (и более аккуратно) использовать одну динамическую хранимую процедуру для обработки ошибок для нескольких других процедур или разделов кода.
Вот пример:
--our error handling procedure
create procedure #myErrorHandling as
begin
print ' Error #'+convert(varchar,ERROR_NUMBER())+': '+ERROR_MESSAGE()
print ' occurred on line #'+convert(varchar,ERROR_LINE())
+' of procedure '+isnull(ERROR_PROCEDURE(),'(Main)')
if ERROR_PROCEDURE() is null --check if error was in MAIN Procedure
print '*Execution cannot continue after an error in the MAIN Procedure.'
end
go
create procedure #myProc as --our test Stored Procedure
begin
BEGIN TRY
print 'This is our Stored Procedure.'
print 1/0 --generate a "Divide By Zero" error.
print 'We will not make it to this line.'
END TRY
BEGIN CATCH
execute #myErrorHandling
END CATCH
end
go
BEGIN TRY --our MAIN Procedure
print 'This is our MAIN Procedure.'
execute #myProc --execute the Stored Procedure
print '*The error halted the procedure, but our MAIN code can continue.'
print 1/0 --generate another "Divide By Zero" error.
print 'We will not make it to this line.'
END TRY
BEGIN CATCH
execute #myErrorHandling
END CATCH
Пример вывода: (этот запрос может быть разветвлен на SEDE здесь .)
This is our MAIN procedure.
This is our stored procedure.
Error #8134: Divide by zero error encountered.
occurred on line #5 of procedure #myProc
*The error halted the procedure, but our MAIN code can continue.
Error #8134: Divide by zero error encountered.
occurred on line #5 of procedure (Main)
*Execution cannot continue after an error in the MAIN procedure.
В области действия блока TRY
/ CATCH
для получения информации об ошибке, вызвавшей выполнение блока CATCH
, можно использовать следующие системные функции:
-
ERROR_NUMBER()
возвращает номер ошибки. -
ERROR_SEVERITY()
возвращает серьезность. -
ERROR_STATE()
возвращает номер состояния ошибки. -
ERROR_PROCEDURE()
возвращает имя хранимой процедуры или триггера, в котором произошла ошибка. -
ERROR_LINE()
возвращает номер строки внутри процедуры, вызвавшей ошибку. -
ERROR_MESSAGE()
возвращает полный текст сообщения об ошибке. Текст содержит значения, предоставленные для любых заменяемых параметров, таких как длина, имена объектов или время.
(Источник )
Обратите внимание ,что существует два типа ошибок SQL: Terminal и уловимые. TRY
/ CATCH
будет [очевидно] ловить только ошибки «Catchable». Это один из многих способов узнать больше о ваших ошибках SQL, но он, вероятно, самый полезный.
Это «better to fail now» (во время разработки) по сравнению с более поздним, потому что, как говорит Гомер . . .
Блоки try-catch для программистов на T-SQL_часть 2
Уровни серьезности и ошибки компиляции.
Давайте, собственно, с severity level, а точнее с их возможных значений и начнем. Как покажут наши дальнейшие исследования, при работе с блоками TRY/CATCH весь диапазон допустимых серьезностей (а это, напомню, все целые числа в диапазоне 0…25 включая обе границы) разбивается на три группы:
- до 10 включительно
- от 11 до 19 включительно
- от 20 до 25 включительно
Поведение TRY/CATCH будет принципиально различным для каждой из трех групп. Давайте смотреть.
Начнем с группы средней, в нем поведение изучаемой конструкции самое предсказуемое:
1 | PRINT ‘—Start—‘ |
Результат:
—Start—
TRY >> Start
CATCH >> Start >> message: **Exception in TRY**
CATCH >> End
—End—
Комментировать особо нечего, все ожидаемо: начинается исполнение пакета → блок TRY начинает свое исполнение → ошибка → блок CATCH начинает свое исполнение и выполняет все свои инструкции → исполнение пакета (batch) продолжается первой инструкцией после метки END CATCH. Все абсолютно точно в согласии с той теорией изложением которой открылся данный труд. Единственное замечание: в показанном скрипте вы можете вместо уровня серьезности 11 выставить любой уровень вплоть до 18-ти включительно и результат будет тем же байт-в-байт. Можно выставить и уровень 19, он, как отмечалось выше, тоже относится к «средней» группе, а потому и при нем результат будет идентичным. Но вот саму строчку «поднимающую» ошибку (строка 4 показанного скрипта) придется чуть дополнить:
4 | RAISERROR (‘**Exception in TRY**’, 19, 1) WITH LOG |
Применение опции WITH LOG продиктовано простым правилом: начиная с уровня серьезности 19 и выше все ошибки без исключения могут «кидаться» только с указанной опцией. Просто потому что с 19-го уровня «игры в песочек» заканчиваются, и каждая ошибка, скорее всего, будет последней вещью которую сделает сервер перед тем как принудительно отключить от себя клиента а то и просто остановиться. А такие ошибки достойны не только внимания отдельного клиента, но и занесения в error log данного экземпляра. Ну и еще в application log на уровне OS Windows, до кучи. И опция WITH LOG, помимо стандартной отправки текста ошибки клиенту, делает еще и две указанные вещи: пишет тот же текст в один лог, и в другой лог. Однако на течение кода, повторюсь, сей факт не влияет совершенно: какие пять строк выводила нам ошибка с уровнем 11, те же пять строк выводит и ошибка с уровнем 19.
Теперь давайте переместимся к первой группе уровней и сменим серьезность на, допустим, 10, не трогая остальной код скрипта. Тогда на выходе у нас:
—Start—
TRY >> Start
**Exception in TRY**
TRY >> End
—End—
Что мы видим? Что, вроде как, и RAISERROR отработал, но и в CATCH мы не «свалились». Да, именно! А это все потому, что ошибки до 10-го уровня включительно — не ошибки. И уж тем более не исключения. А информационные сообщения. О которых нужно, собственно, информировать клиента, то есть послать ему текст такой «псевдо-ошибки» и, иногда, код и состояние ее же. А в CATCH уходить не надо — ошибки-то нет!
И, наконец, ошибки третьей группы. Исправляем уровень серьезности на 20, не забыв, разумеется, включить опцию WITH LOG и получаем:
—Start—
TRY >> Start
Msg 2745, Level 16, State 2, Line 4
Process ID 52 has raised user error 50000, severity 20. SQL Server is terminating this process.
Msg 50000, Level 20, State 1, Line 4
**Exception in TRY**
Msg 0, Level 20, State 0, Line 0
A severe error occurred on the current command. The results, if any, should be discarded.
И мало того, если посмотреть на ярлык той закладки редактора в которой мы только что исполняли наши скрипты то мы увидим, что она not connected. И вновь такое поведение полностью запланировано, ведь ошибки уровня 20-го и выше считаются фатальными (fatal) и сервер безусловно рвет соединение с тем клиентом, в чьей сессии произошла подобная фатальная ошибка. Причем рвется соединение тут же, по обнаружении ошибки. Никакого шанса на ее корректировку сервер не оставляет.
Итого, как это ни странно, но ошибки вроде бы находящиеся на разных полюсах серьезности (до 10-го уровня и от 20-го) обрабатываются блоком TRY/CATCH идентично, а именно — никак не обрабатываются. Только причины тому совершенно разные. В одном случае это недостаточная серьезность что бы прерывать течение основного алгоритма, в другом — серьезность избыточная, в силу которой исполнение прерывается слишком рано и слишком грубо. И, таким образом, только ошибки из диапазона серьезности 11-19 могут извлечь какой-то «профит» из обсуждаемой конструкции. Справедливости ради заметим, что большинство «ожидаемых» ошибок (обозначим их так) вполне укладывается в этот диапазон. Да, собственно говоря, там по большому счету только уровень 16 играет решающую роль, прочие уровни серьезности погоды не делают. Допустим всего сервер версии 2008R2 нам может вернуть 76 различных ошибок уровня 11. Или 69 ошибок уровня 14. Или 288 уровня 15. А ошибок уровней 12 и 13 так и вовсе по одной (!) на каждый уровень. А вот вот ошибок уровня 16 — 6400, круглым счетом. Отсюда понятно, почему новая инструкция THROW упомянутая чуть ранее решила более никакой серьезностью не морочиться, а все пользовательские ошибки складывать в этот же «мейнстрим». Тем не менее, для нас важен лишь вот такой промежуточный итог:
Конструкция TRY/CATCH языка T-SQL способна обработать только ошибки уровня серьезности от 11 до 19 включительно.
Теперь давайте вот такой скрипт разберем:
1 | PRINT ‘—Start—‘ |
В нем, как легко заметить, две проблемы: таблицы TableThatNotExist, как можно предположить из названия, не существует, и слово SELLECT может быть чем угодно, но только не инструкцией языка T-SQL. Там, конечно, еще ключевое слово FROM пропущено, но это уж сущие мелочи. Пробуем исполнить:
1 | Msg 102, Level 15, State 1, Line 4 |
Очевидно что не только не срабатывает TRY/CATCH, но и пакет (batch) как таковой не начинает исполняться. Тут все дело в том, что показанная ошибка — не ошибка времени исполнения, она — времени компиляции пакета. Убедиться в том очень легко — достаточно в студии нажать Ctrl+F5, а не просто F5, или, что эквивалентно, нажать на панели инструментов кнопку Parse. Сообщение будет тем же самым, хотя выполнение в таком случае и начаться не могло. Так вот что бы пакет как целое в принципе мог стартовать, абсолютно все входящие в его состав операторы должны быть валидны, или, если вы предпочитаете практические умозаключения, нажатие Ctrl+F5 должно возвращать:
Command(s) completed successfully.
В этом, и только в этом случае исполнение начнется и у TRY/CATCH появится хотя бы потенциальная возможность проявить себя. Ну а поскольку в показанном примере наш batch даже не стартует — чего же мы ждем? В то же время не надо думать, что ошибка компиляции это некая грандиозная ошибка с уровнем серьезности 5 тысяч. Да нет, как показывает сообщение чуть выше серьезность у нее самая обычная, 15-я. «Видали и покруче», как говорится. И поэтому TRY/CATCH перехватить такую ошибку вполне может (тем паче, что она вполне укладывается в требуемый диапазон 11-19), важно лишь что бы этот блок уже работал, в то время как будет происходить эта ошибка. Такая диспозиция возможна лишь в одном случае: блок TRY/CATCH находится на более высоком уровне исполнения, нежели пакет содержащий синтаксическую ошибку. Непонятно? Вот вам пример:
1 | PRINT ‘—Start—‘ |
Результат:
—Start—
TRY >> Start
CATCH >> Start >> message: Incorrect syntax near ‘*’.
CATCH >> End
—End—
В данном случае компиляция исходного пакета проблем не вызывает, переменная @n есть просто строка могущая содержать совершенно любую ересь, компилятор ее содержимое не анализирует. Так что пакет компилируется, блок TRY/CATCH включается в работу, и лишь после этого выясняется что указанная переменная должна содержать вовсе не ересь, а валидный T-SQL код. Ключевое слово тут — после этого. Блок уже работает, и лишь тут случается компиляция операторов уровнем ниже, например потому что эти операторы оформлены в виде динамического SQL. Ошибка компиляции прекрасно перехватывается и мы можем делать с нею все что захотим.
Хорошо, давайте исправим весь синтаксис, но несуществующую таблицу оставим:
1 | PRINT ‘—Start—‘ |
Результат:
—Start—
TRY >> Start
Msg 208, Level 16, State 1, Line 4
Invalid object name ‘TableThatNotExist’.
Хм. Вроде как и блок TRY начал исполняться, но все же ошибка не наша, не из CATCH-а. Обычная, системная ошибка. Которая, кстати, прервала исполнение всего пакета, а если бы мы «свалились» бы в CATCH, то был бы напечатан по крайней мере финальный —End—. Да, здесь уже проблема упирается не в компиляцию, Ctrl+F5 подтверждает нам что с нею-то все в порядке. Тут все крутится вокруг иного механизма — отложенного разрешения имен (deferred name resolution), который в целом является «тем еще» механизмом. Дело в том, что движок занимаясь парсингом текста в пакете и натыкаясь на несуществующий объект (как раз наш случай) не засчитывает автоматически сложившуюся ситуацию как ошибочную. И никаких предупреждений на этапе компиляции вы не получаете. Движок справедливо (по крайней мере с точки зрения команды разработчиков SQL Server это — справедливо, хотя автор строк читаемых вами в настоящий момент готов с ними поспорить) полагает, что течение кода в пакете может быть построено таким хитрым образом, что когда дело дойдет до реального обращения к объекту последний существовать уже будет! Допустим одна из строк пакета может вызвать хранимую процедуру, которая и создаст требуемый объект. Так что парсер оставляет нашу TableThatNotExist в покое на некоторое время, «допарсивает» остальные строки пакета, и, поскольку уж с ними-то вообще полный OK, дает всему пакету «отмашку» на исполнение. Начинает исполняться пакет → начинает исполняться блок TRY → и тут — SELECT. Со все еще несуществующей таблицей. Вот тут-то случается финальное разрешение имен которое никакой однозначности не допускает. И вновь по «условиям игры», TRY/CATCH не способен перехватить ошибки такого финального разрешения имен если оно происходит на одном уровне исполнения с указанным блоком. У нас TRY/CATCH исполняется в пакете и в нем же происходит разрешение имен — полный провал, блок бесполезен. Однако, как вновь не сложно заметить, ошибка разрешения имен самая обычная, 16-го уровня серьезности, так что… шансы есть. Попробуем?
1 | PRINT ‘—Start—‘ |
И в этот раз:
—Start—
TRY >> Start
CATCH >> Start >> message: Invalid object name ‘TableThatNotExist’.
CATCH >> End
—End—
А все потому, что динамический SQL определяет свой, отдельный пакет в котором все свое: своя компиляция, свое отложенное разрешение имен, свое финальное разрешение имен и т.д. И блок TRY/CATCH оказывается «сверху» всех этих «своих». Отсюда и результат, которого, кстати, можно добиться и вот как еще:
1 | USE tempdb go IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE SPECIFIC_NAME = ‘A’) DROP PROCEDURE A go CREATE PROCEDURE A PRINT ‘—Start—‘ |
Совершенно аналогично, финальное разрешение имен происходит на уровень ниже, чем тот на котором работает TRY/CATCH. Последний прекрасно себя чувствует и оказывается очень даже «при делах». Хранимая процедура представляет собой набор инструкций исполняемых в отдельном пакете, а TRY/CATCH работают в пакете уровнем выше. В целом же, автор блога согласен с резкой критикой отечественных и зарубежных SQL-профессионалов в адрес отложенного разрешения имен, позволяющего указывать несуществующие таблицы. Нельзя сказать, что у данного подхода 100% минусов и ни единого плюса. Есть и они, конечно. Но недостатки значительно перевешивают достоинства, на что было указано неоднократно. Для борьбы с ними, как минимум, можно было бы начать с опции с помощью которой программист на языке T-SQL мог бы сам себе перекрывать доступ к обсуждаемой «фиче» сомнительной ценности, и требовать от себя же безусловного наличия всех объектов на которые он ссылается в момент парсинга таких ссылок. Но пока — увы, увы — «фича» неотключаема, «радует» нас всех, вне зависимости рады вы ей или нет, и, похоже, вообще не собирается уходить на заслуженный (?) отдых. Так что сейчас и в обозримом будущем нет иного выхода кроме как знать про нее и про кучу совсем неочевидных нюансов вытекающих из факта ее существования. Пару пунктов из этой кучи неочевидностей мы рассмотрели только что в контексте нашего разговора, хотя в целом это тема отдельной заметки если не статьи.
Вложенные блоки TRY/CATCH и блоки TRY/CATCH различных областей.
Вполне ожидаемо, обсуждаемая нами конструкция может быть вложена. Иными словами, любая инструкция в любом из двух блоков может быть, на самом деле, составной инструкцией выраженной через внутренний блок TRY/CATCH. А у этого внутреннего блока может быть свой, еще более вложенный блок, и т.д. Еще раз подчеркиваю (почему-то многих SQL-программистов на этом месте начинают терзать сомнения) — любая инструкция в любом из блоков (сомнения наших программистов связаны, разумеется, с CATCH блоком, так вот и в нем тоже можно!) может быть отдельной парой TRY/CATCH. О вложенности в блок CATCH мы поговорим в следующем разделе, а этот посвятим полностью вложениям в блок TRY. Начнем с примера:
1 | PRINT ‘—Start—‘ |
Результат вполне ожидаем:
—Start—
TRY_1 >> Start
TRY_2 >> Start
TRY_3 >> Start
CATCH_3 >> Start >> message: **Exception in TRY_3**
CATCH_3 >> End
CATCH_2 >> Start >> message: **Exception in TRY_2**
CATCH_2 >> End
CATCH_1 >> Start >> message: **Exception in TRY_1**
CATCH_1 >> End
—End—
Иными словами, как мы того и ждем интуитивно, исключение произошедшее в блоке TRY ловится тем блоком CATCH, что составляет пару к предыдущему. Вообще-то, все происходит настолько очевидно, что можно было бы и не тратить время на пример если бы он не был переходным звеном к теме близкой к вложенным блокам, но все-таки теме отдельной — TRY/CATCH блоки различных областей. Вот она безусловно заслуживает отдельного разбирательства.
Разумеется, сначала выясним доподлинно — а что такое область? Это, по счастью, совсем просто: область, она же scope, есть отдельный программный модуль, то есть или хранимая процедура, или триггер, или функция или пакет (batch). Если области никак друг с другом не связаны и используют в своем коде блоки TRY/CATCH то ничего нового помимо уже сказанного добавить мы не можем: TRY в каждом таком модуле будет (возможно) «бросаться» исключениями, кои и будут успешно перехватываться парным ему блоком CATCH. Самые «интересности» начинаются когда один модуль в своем коде вызывает другой, а тот — третий и при этом часть модулей написано с привлечением технологии обработки ошибок, а часть — без нее. Снова смотрим:
1 | USE tempdb go IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE SPECIFIC_NAME = ‘A’) DROP PROCEDURE A IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE SPECIFIC_NAME = ‘B’) DROP PROCEDURE B IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE SPECIFIC_NAME = ‘C’) DROP PROCEDURE C GO CREATE PROCEDURE C EXECUTE A |
Итог:
A >> —Start—
A >> TRY >> Start
B >> —Start—
B >> TRY >> Start
C >> —Start—
C >> TRY >> Start
C >> CATCH >> Start >> message: **Exception in C**
C >> CATCH >> End
C >> —End—
B >> TRY >> End
B >> —End—
A >> TRY >> End
A >> —End—
Анализируем: у нас три модуля (иными словами — три области) — хранимые процедуры A,B,C, причем A вызывает B, а та, в свою очередь, C. Каждая процедура берет на вооружение перехват ошибок и вызов указанных процедур случается только внутри блоков TRY. Проблемным является код самой «вложенной» процедуры — C, именно он «кидается» исключением. Однако поскольку блоки TRY/CATCH той же самой процедуры C ошибку обрабатывают и подавляют — никто из «вызывателей» никогда не узнает что проблемы имели место быть. Процедуры A и B полагают что у нас «полный OK» и спокойно исполняют все свои инструкции, как внутри блоков TRY так и за их пределами. Теперь изменим код процедуры C отказавшись от обработки ошибок в ее пределах:
1 | … |
Тогда:
A >> —Start—
A >> TRY >> Start
B >> —Start—
B >> TRY >> Start
C >> —Start—
B >> CATCH >> Start >> message: **Exception in C**
B >> CATCH >> End
B >> —End—
A >> TRY >> End
A >> —End—
Обратите внимание — исключение все-равно перехвачено! Только озаботилась этим процедура «вышестоящая», B. Однако в отличии от предыдущего варианта это не прошло бесследно для нее — блок TRY процедуры B не смог полностью завершиться. Иными словами, B узнала о проблемах в C и, образно говоря, это стали «ее проблемы». Строка EXECUTE C стала последней выполненной в блоке TRY процедуры B. Однако процедура A по-прежнему считает что у нас «полный OK». Попробуем оставив процедуру C в ее текущей редакции произвести аналогичные изменения в процедуре B:
1 | … |
Новый тест:
A >> —Start—
A >> TRY >> Start
B >> —Start—
C >> —Start—
A >> CATCH >> Start >> message: **Exception in C**
A >> CATCH >> End
A >> —End—
Исключение все еще перехватывается но уже на самом верхнем уровне (читай — области) — в процедуре A. Теперь уже ни одна из процедур не может говорить про OK, все три в той или иной степени «пострадали» от проблем процедуры C. Наконец — уберем обработку ошибок и в A тоже:
1 | … |
Финальный тест:
A >> —Start—
B >> —Start—
C >> —Start—
Msg 50000, Level 11, State 1, Procedure C, Line 5
**Exception in C**
C >> —End—
B >> —End—
A >> —End—
Теперь об ошибке узнали «на самом верху» — в клиенте (в нашем случае — в студии). Поскольку блоки TRY не используются и ошибка не является фатальной (уровень серьезности меньше 20) сами хранимые процедуры выполняются до конца. Если бы ошибка была бы не эмулируема с помощью RAISERROR, а была бы настоящей системной ошибкой вроде ошибки конвертации типов, исполнение всего скрипта прервалось на этой самой проблемной строке кода. Однако видя «нестрашную» пользовательскую ошибку с кодом 50000 движок разрешает коду продолжать свое исполнение. Отметим, что блок TRY/CATCH совладал бы и с более серьезной системной ошибкой вроде упомянутой ошибки конвертации, но при условии, разумеется, если серьезность такой системной ошибки будет меньше 20.
Какое же умозаключение можно вывести из серии последних опытов? Умозаключение такое: движок сервера пытается найти обработчик в каждой из областей, двигаясь от области самой «внутренней» (где, собственно, ошибка и была замечена), к области самой «внешней». Первый же найденный обработчик будет «назначен» ответственным за перехват исключения. Если ни одна из областей, включая самую «внешнюю», с которой началось исполнение кода, не смогла предоставить обработчик, то ошибка безусловно отправляется клиенту, а код или продолжает свое исполнение или прекращает исполнение немедленно, в зависимости от типа и серьезности каждой конкретной ошибки. Для программистов языков высокого уровня все предыдущие предложения текущего абзаца можно было заменить фразой «стек вызовов раскручивается (unwind the call stack) до обнаружения первого блока CATCH», и они бы все поняли, но мы-то пишем материал для программистов на T-SQL, верно? А поэтому — не будем экономить на пояснениях, с задачей подобной экономии уже прекрасно справилось множество авторов до нас.
Итак, мы убедились, что герой нашего повествования весьма гибок и эластичен: его можно «запихивать» внутрь себя же как непосредственно, в границах одной области исполнения, так и делать тоже самое косвенно, если мы имеем дело с несколькими областями. И в том, и в другом случае вложенные TRY/CATCH ведут себя похоже, однако, как говорится, «есть нюансы».
Вложение блоков TRY/CATCH в блок CATCH предыдущего уровня.
Как было обещано в предыдущем разделе, теперь мы отдельно разберем специфику блоков вложенных в блок CATCH. Вы уже знаете, что такое вполне возможно технически и синтаксически, но — зачем? Почему мы вообще можем захотеть городить подобные «многоэтажные» конструкции во втором из пары блоков? Дело тут в следующем.
Строго говоря, блок CATCH предназначен исключительно для обработки ошибок. То есть провели логирование произошедшего, возможно сформировали для клиента «подменное» сообщение, отправили его «наверх», и — все, END CATCH. И говоря не менее строго все операторы обсуждаемого блока не должны допускать возникновения ошибок даже теоретически. Можно и так сказать: идеальный блок CATCH содержит операторы PRINT и никаких других. И тогда блок CATCH будет при любых обстоятельствах 100% работоспособен. А почему нам так важно исключить свои ошибки в данном блоке? А потому, что раз мы в нем оказались, то у нас уже есть «ошибка 1» из блока TRY. Предлагаете «лакировать» ее сверху «ошибкой 2» из блока CATCH? Ну да, наш условный T-SQL программист обожает «шарады с подтекстом», ага. Поэтому если у CATCH нет исходного, преднамеренного умысла скрыть «ошибку 1» код этого блока не должен «путаться под ногами» и мешать нам «ребусы разгадывать». Возможно ли было достижение такой цели включением в BOL строки: «тело блока CATCH допускает единственный оператор — PRINT»? Разумеется. А было бы это хорошо? Ну… не очень, прямо скажем. Если нам нужно внести информацию о случившемся в предусмотренную для этих целей лог-таблицу, то что — никак? А ведь такой функционал более чем востребован. А вручную «прирезать» место к журналу транзакций и вновь повторить операцию вставки строк — снова не? Гм… а вообще обсуждаемая конструкция нам тогда точно нужна? Вот именно. При обсуждаемом возможном подходе идея обработки ошибок получается крайне надежной, но и столь же крайне негибкой. А поэтому нам как бы сказали: творите в CATCH чего вам заблагорассудиться, но только, по причинам изложенным выше, «оборачивайте» «опасные операторы» этого блока в свои блоки TRY/CATCH. Хороший пример, близкий к воплощению обсуждаемой концепции «безопасный многоэтажный CATCH» в реальном коде, можно найти в системе обработки ошибок учебной базы AdventureWorks2008R2 (если у вас ее нет, то ссылку на соответствующий инсталляционный пакет вам любезно предоставит страница Скачать данного блога). Высокоуровневый подход реализованного там решения такой:
- есть таблица ErrorLog выделенная исключительно под лог случившихся ошибок;
- есть хранимая процедура uspLogError вставляющая всю необходимую информацию в указанную таблицу;
- программист создающий код для данной учебной базы и желающий воспользоваться готовым функционалом просто вызывает EXECUTE uspLogError. Обо всем остальном позаботится эта самая процедура. Однако указанный вызов обязан производится программистом исключительно в своем блоке CATCH и нигде более. Так же он должен позаботится о корректном решении вопроса открытой транзакции (если он таковую открывал, конечно), однако поскольку тема «TRY/CATCH и транзакции» у нас впереди, то на текущий момент можно упростить ситуацию и сказать, что нашему программисту требуется в любой строчке внутри блока CATCH выполнить указанный EXECUTE. С большой степенью вероятности этот EXECUTE будет вообще единственной строкой его блока CATCH, если только ему не нужно дополнительно «чего-то этакого»;
- процедура uspLogError уже гарантировано находясь в блоке CATCH все же открывает свой внутренний TRY/CATCH. Зачем? Затем, что она пытается вставить строчку в упомянутую таблицу, а любой INSERT — «потенциальная опасность»;
- если все вставилось — отлично, CATCH внутри uspLogError игнорируется, управление возвращается в CATCH на более высоком уровне и т.д.;
- если же INSERT исполнить не удалось (причины этой неудачи не столь важны) — срабатывает «CATCH вложенный в CATCH». И вот он уже содержит PRINT-ы и только их (технически говоря этот «CATCH в CATCH» «дергает» еще одну хранимую процедуру, uspPrintError, но вот эта последняя состоит ровно из двух PRINT-ов и иных инструкций не содержит). Просто потому, что хватит уже ошибки множить.
То есть, в несколько упрощенно-графическом виде мы имеем вот какую конструкцию:
Понятно, что все будет работать, если вы коды процедур uspLogError/uspPrintError «развернете» и просто вставите в свой собственный CATCH блок. Просто в результате у вас будет многоэтажная, плохочитаемая конструкция исключающая ее повторное применение в других CATCH блоках. Но чисто технически — нет проблем, хотите — «разворачивайте». В целом же систему обработки ошибок базы AdventureWorks2008R2 можно брать если не как готовое решение для промышленного кода, то уж как шаблон такого решения несомненно. Ну а общий вывод такой: блок CATCH почти всегда будет содержать внутренний TRY/CATCH потому, что мы весьма редко удовлетворимся банальным PRINT-ом в коде этого блока, а почти наверняка захотим проводить там операции далеко не столь «безобидные». А неконтролируемая «эскалация исключений» последнее чего мы добиваемся в нашей профессиональной карьере.
Проблема «двойной ошибки».
Большинство ошибочных ситуаций в SQL Server приводит к получению нами одного конкретного сообщения. Захотели разделить на ноль, а нам: Divide by zero error encountered. Захотели привести букву ‘A’ к типу int, а нам: Conversion failed when converting… . И так далее. Это «нормальные» сообщения о «нормальных» ошибках.
В тоже время, ряд ошибок (по счастью их число очень незначительно) приводит к получению сразу двух сообщений. Со своими текстами, номерами, а изредка и со своими уровнями серьезности. Вот вам пример такой «нестандартной» ошибки:
1 | USE tempdb |
Результат:
Msg 4917, Level 16, State 0, Line 1
Constraint ‘C2’ does not exist.
Msg 4916, Level 16, State 0, Line 1
Could not enable or disable the constraint. See previous errors.
Назначение сообщения 4916 решительно непонятно. Нельзя включить/выключить ограничение? Ну так если 4917-ая ошибка нам уже сказала, что ограничение отсутствует и даже указала какое именно по его имени, то, как бы все ясно, нет? Ну да в показанном примере это беда не столь «большой руки», на 4916 можно просто закрыть глаза и пользоваться информацией полезной.
Все приобретает куда более суровые формы, при «оборачивании» таких «продвинутых» ошибок в блок TRY/CATCH:
1 | USE tempdb |
На выходе:
—Start—
TRY >> Start
CATCH >> Start >> message: Could not enable or disable the constraint. See previous errors.
CATCH >> End
—End—
Нда, как говорится — «удружили», спасибо. «Смотрите предыдущее сообщение», только мы вам его не покажем, ага. А это все потому, что функции извлечения информации об ошибке приведший код в блок CATCH работают все строго с последней из зафиксированных ошибок. Ровно как поступала в свое время приснопамятная функция @@ERROR. А последняя в данной ситуации именно та самая бесполезная 4916. Если бы пара ошибок поступала от сервера «на выход» в обратном порядке было бы отлично, но… что есть — то есть. И вот в такой ситуации автор посоветовать вам, своим читателям, решительно ничего не может. Да и думаю не только автор. А что тут скажешь? Отбрасываются «предыдущие» ошибки, и хоть ты что делай. Насколько помнится автору, на Microsoft Connect давненько уже висит «реквест» что бы в блоке CATCH было позволено обходить «стек ошибок», а не только выбирать его верхний элемент. Но… воз и ныне там. Поэтому на текущий момент ситуация несколько бредовая: в редких случаях (и давайте порадуемся хотя бы тому, что случаи эти действительно редки, и даже очень) для получения информации об ошибке нужно снять ту «обертку», которая по замыслу ее создателей должна нас снабжать самой полной информацией о ней, об ошибке. То есть проще говоря привести последний скрипт к предпоследнему и лишь тогда увидеть «корень» проблемы. Ну а кто обещал что легко будет?
Заметки Дмитрия Пилюгина о Microsoft SQL Server
Возможно, не все знают, что в t-sql конструкция try catch обрабатывает не все ошибки.
Это поведение хорошо документировано, но может стать сюрпризом для людей привыкших работать с классическим try/catch в объектно-ориентированных языках.
Подробно это описано в документации TRY…CATCH (Transact-SQL), Использование конструкции TRY…CATCH в языке Transact-SQL — я же просто приведу некоторые примеры, на которые наталкивался сам.
create function dbo.uf_NotExistsingFunc() returns char(1) as begin return 'Y' end go --просто убедимся что блок catch написан верно и ловит ошибки begin try select 1/0 end try begin catch print('поехали!') end catch go --не ловит (опечатались в названии функции) begin try select dbo.uf_otExistsingFunc() end try begin catch print('опечатались в названии функции - catched!') end catch go --не ловит (не существующая временная таблица) begin try select * from #NotExistsingTable end try begin catch print('не существующая временная таблица - catched!') end catch go --не ловит (не существующая таблица) begin try select * from dbo.NotExistsingTable end try begin catch print('не существующая таблица - catched!') end catch go --ловит (не существующая процедура) begin try exec dbo.NotExistsingProc end try begin catch print('не существующая процедура - catched!') end catch go --ловит (ошибка в динамике/вызываемой процедуре) begin try exec ('select * from NotExistsingTable') end try begin catch print('ошибка в динамике/вызываемой процедуре - catched!') end catch go drop function dbo.uf_NotExistsingFunc go
результат
(0 row(s) affected) поехали! Msg 195, Level 15, State 10, Line 3 'uf_otExistsingFunc' is not a recognized built-in function name. Msg 208, Level 16, State 0, Line 3 Invalid object name '#NotExistsingTable'. Msg 208, Level 16, State 1, Line 3 Invalid object name 'dbo.NotExistsingTable'. не существующая процедура - catched! ошибка в динамике/вызываемой процедуре - catched!
Это некоторые частные случаи, которые встречались мне.
Классификация всех таких случаев и более подробное описание есть в документации. Надеюсь, что эта небольшая заметка cподвигнет людей не сталкивавшихся с этим заглянуть в BOL, чтобы ознакомиться подробнее и иметь это ввиду при написании кода на t-sql.
Также рекомендую ознакомиться с книгой Defensive Database Programming By Alex Kuznetsov, которая доступна в виде бесплатного PDF на сайте redgate.
В ней хорошо рассказывается про подобные подводные камни при обработке ошибок, а также разбираются многие другие интересные темы.
Productivity Features for SSMS and Visual Studio in SQL Complete
Tabs coloring
Helps you easily define the server connection a tab is currently related to by the matching color.
With this functionality, you can simply assign a color to the environment for the servers related to development, production,
sandbox or test. Additionally, it is possible to add or delete Color Match by clicking the corresponding buttons on the Settings
section of the Options menu.
Documents sessions
The functionality monitors active open sessions, thus, allowing you to restore accidentally closed or unsaved tabs within the current IDE session.
Custom SSMS main window title
You can change or define your own naming pattern for the SSMS main window caption and SQL document tabs.
This way a proportional tab width is achieved so that you can control what is displayed in the tab name.
Restore last closed tab
Easily opens recently closed tabs within one SSMS session, including the unsaved ones.
Close unmodified tabs
The functionality closes the tabs that were not modified during your current MSSMS session.
Stores main information about executed SQL statements for a particular period.
You can view, edit, and search the queries you run in the database.
Additionally, you can monitor who executed a query and when, as well as other valuable information.
Run script on multiple databases
Select databases on the current server and execute a script against them from one query window.
You can also specify the mode, in which the script will be executed for the selected databases: parallel or sequential.
Document Outline window
It displays a current document structure and considerably simplifies navigation in large SQL documents.
You can synchronize the structure with text directly from the code.
Execute current statement
You can execute a specific SQL statement without selecting it. You just need to place the mouse pointer over the statement
and press Ctrl+Shift+E or Alt+Enter, and dbForge SQL Complete will define the statement boundaries itself.
Execute to cursor
A handy feature allowing you to execute a script to the current position of the cursor.
Go to definition for database objects
Navigate from Code Editor straight to a specific object in Object Explorer (Server Explorer in Visual Studio).
Decrypt encrypted objects
dbForge SQL Complete easily handles encrypted objects, it shows the DDL of an encrypted object in a separate SQL document.
Highlight occurrences of identifiers
If you click an identifier in your code, SQL Complete will highlight all other occurrences of this identifier within your statement with grey color. The feature helps quickly discover where the current identifier is used.
Highlight BEGIN and END
If you click a BEGIN or END delimiter in your code, SQL Complete will highlight the BEGIN/END syntax pair in your statement with grey color. The feature helps quickly find matching BEGIN and END statements in a query.
Highlight BEGIN TRY/END TRY
The matching BEGIN TRY/END TRY statements will be highlighted with grey color after you click one of them. The feature significantly facilitates coding by helping quickly find matching statements in long and complex queries.
Highlight BEGIN CATCH/END CATCH
Discover matching BEGIN CATCH/END CATCH pairs in complex queries. After clicking one of the statements, the syntax pair will be highlighted.
Highlight CASE and END
Highlighting matching CASE and END operators can significantly reduce your coding time as CASE expressions used in SQL syntax can be quite long and navigating through them is not an easy task.
Highlight COLUMNS and VALUES in INSERT statement
SQL Complete highlights the name of the columns and its corresponding value in the INSERT statements to help you insert valid values. The feature significantly facilitates writing queries to insert values into multiple columns.
When you work with large scripts, SQL Complete will help you quickly find the beginning of an SQL statement.
Just press a certain hotkey combination and the cursor will sequentially jump to the beginning of each statement in the SQL document.
Find matching BEGIN/END pairs in complex queries. By pressing SHIFT+F12, your cursor will jump up or down to the matching keywords.
When working with large scripts, it is important to be able to quickly navigate between paired keywords in an SQL statement.
With SQL Complete you can jump between BEGIN TRY and END TRY in a blink.
Find matching BEGIN CATCH/END CATCH pairs in complex queries. By pressing Shift+F12, your cursor will jump up or down to the matching keywords.
The CASE expressions used in statements can be quite long and navigating between their beginnings and ends can be a daunting task.
To solve this problem, jumping between CASE and END has been introduced.
You can quickly jump between matching brackets within a statement by pressing Ctrl+]. This SSMS built-in feature helps save time and energy when writing long and complex queries.
In large INSERT statements, it’s often hard to determine which value corresponds to which column, and vice versa. This functionality will help you
quickly locate a corresponding value for the current column.
Generate CREATE/ALTER script for server objects
This option lets you generate a script for object’s modification after you drag the carriage to that object.
Generate CRUD
Quickly generate CRUD (CREATE, READ, UPDATE, DELETE) stored procedures for tables using customizable procedure
that can be changed to better suit your needs.
Inline EXEC
Unwraps the sp_executesql procedure into a neat and readable static SQL query.
Convert EXEC to script
This feature allows to simplify debugging by replacing the call to a stored procedure with the stored procedure body.
It takes the contents of the stored procedure and replaces the call in your query with them.
Convert Inline EXEC to Script
Unwraps the sp_executesql procedure into a neat and readable static SQL query.
Execution warnings
This feature analyzes potentially dangerous statements (DELETE, DROP, TRUNCATE, and UPDATE) and generates
a pop-up alert if a user is about to execute a statement that may cause data loss. For example, the execution
warning will be thrown up if you try to execute a DELETE statement with no WHERE clause.
Execution notifications
When a query is successfully executed, a pop-up dialog box notifies you about the time of execution.
If the query was running more than 23:59:59 hours, days are added to the time in the Execution Notification.
Transaction reminder
Whenever there open transactions during query execution,
a reminder will pop-up informing you about the number of uncommitted transactions.
Generate Script As from SSMS data grid
You can generate a script from the SSMS grid based on the data in the table for the following statements: INSERT, INSERT #tmpTable,
WHERE IN(), WHERE AND OR, UPDATE. You can select the result statement to be saved to a file, copied to a clipboard, or opened in a new window.
Copy data from SSMS grid to file or clipboard
The data from a cell or entire table can be copied from the results grid to a file or clipboard in any of the available formats (CSV, XML, HTML, JSON).
Grid aggregates
Aggregates are automatically displayed at the bottom of the SSMS Results Grid. Select a range of values in the Results Grid to calculate aggregates.
On the Aggregate Panel, you will see MAX, MIN, AVG, SUM, COUNT, DISTINCT ON for these values. To copy a value from the Aggregate Panel,
double-click it or select it with a cursor and press Ctrl+C. Alternatively, double-click the value you want to copy and select the Copy command from the context menu that appears.
Data visualizers
In the Results Grid, data in Hexadecimal, Text, XML, HTML, Rich Text, PDF, JSON, Image, Spatial formats may be visually represented via the Data Viewer window.
Data can also be conveniently saved as a file. For example, hex data shown in a grid can be saved as a JPEG image.
Find in Results Grid
Searches all the matching data in the grid by the specified value.
Extend the search by adding the following search details:
Match Case to set a case-sensitive search
Whole Words to look for the single words
Use Regular Expressions to set the search by Reg Exp
Close button to close the search bar
All the data found will be highlighted in the Results Grid.
TRY … CATCH (Transact-SQL) — SQL Server
- 10 минут на чтение
В этой статье
Применимо к: SQL Server (все поддерживаемые версии) База данных SQL AzureAzure SQL Managed InstanceAzure Synapse Analytics Хранилище параллельных данных
Реализует обработку ошибок для Transact-SQL, аналогичную обработке исключений в языках Microsoft Visual C # и Microsoft Visual C ++.Группа операторов Transact-SQL может быть заключена в блок TRY. Если в блоке TRY возникает ошибка, управление передается другой группе операторов, заключенной в блок CATCH.
Соглашения о синтаксисе Transact-SQL
Синтаксис
НАЧАТЬ ПОПРОБОВАТЬ
{sql_statement | statement_block}
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
[{sql_statement | statement_block}]
КОНЕЦ ЗАХВАТ
[; ]
Аргументы
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
вызывается хранимая процедура и возвращается информация об ошибке.
- Убедитесь, что хранимая процедура еще не существует.ЕСЛИ OBJECT_ID ('usp_GetErrorInfo', 'P') НЕ ПУСТО
ПРОЦЕДУРА УДАЛЕНИЯ usp_GetErrorInfo;
ИДТИ
- Создать процедуру для получения информации об ошибке.
СОЗДАТЬ ПРОЦЕДУРУ usp_GetErrorInfo
В КАЧЕСТВЕ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки
, ERROR_SEVERITY () AS Уровень серьезности ошибки
, ERROR_STATE () как ErrorState
, ERROR_PROCEDURE () как ErrorProcedure
, ERROR_LINE () AS 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
внутри хранимой процедуры.
НАЧАТЬ ПОПРОБОВАТЬ
- Таблица не существует; разрешение имени объекта
- ошибка не обнаружена.
ВЫБРАТЬ * ИЗ NonexistentTable;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки
, ERROR_MESSAGE () AS ErrorMessage;
КОНЕЦ ЗАХВАТ
Ошибка не обнаружена, и управление переходит из TRY...CATCH
построить на следующий более высокий уровень.
Выполнение инструкции SELECT
внутри хранимой процедуры приведет к возникновению ошибки на уровне ниже блока TRY
. Ошибка будет обработана конструкцией TRY ... CATCH
.
- Убедитесь, что хранимая процедура не существует.
ЕСЛИ OBJECT_ID (N'usp_ExampleProc ', N'P') НЕ ПУСТО
ПРОЦЕДУРА УДАЛЕНИЯ usp_ExampleProc;
ИДТИ
- Создайте хранимую процедуру, которая вызовет
- ошибка разрешения объекта.СОЗДАТЬ ПРОЦЕДУРУ usp_ExampleProc
В КАЧЕСТВЕ
ВЫБРАТЬ * ИЗ NonexistentTable;
ИДТИ
НАЧАТЬ ПОПРОБОВАТЬ
ВЫПОЛНИТЬ usp_ExampleProc;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки
, ERROR_MESSAGE () AS ErrorMessage;
КОНЕЦ ЗАХВАТ;
Незавершенные транзакции и XACT_STATE
Если ошибка, сгенерированная в блоке 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 () как номер ошибки
, ERROR_SEVERITY () AS Уровень серьезности ошибки
, ERROR_STATE () как ErrorState
, ERROR_PROCEDURE () как ErrorProcedure
, ERROR_LINE () AS ErrorLine
, ERROR_MESSAGE () AS ErrorMessage;
КОНЕЦ ЗАХВАТ;
ИДТИ
Б.Использование TRY … CATCH в транзакции
В следующем примере показано, как блок TRY ... CATCH
работает внутри транзакции. Оператор внутри блока TRY
генерирует ошибку нарушения ограничения.
НАЧАТЬ СДЕЛКУ;
НАЧАТЬ ПОПРОБОВАТЬ
- Сгенерировать ошибку нарушения ограничения.
УДАЛИТЬ ИЗ ПРОИЗВОДСТВА.
ГДЕ ProductID = 980;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки
, ERROR_SEVERITY () AS Уровень серьезности ошибки
, ERROR_STATE () как ErrorState
, ERROR_PROCEDURE () как ErrorProcedure
, ERROR_LINE () AS ErrorLine
, ERROR_MESSAGE () AS ErrorMessage;
ЕСЛИ @@ TRANCOUNT> 0
ОТКАТНАЯ СДЕЛКА;
КОНЕЦ ЗАХВАТ;
ЕСЛИ @@ TRANCOUNT> 0
ЗАВЕРШИТЬ СДЕЛКУ;
ИДТИ
С.Использование 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 () как номер ошибки
, ERROR_SEVERITY () AS Уровень серьезности ошибки
, ERROR_STATE () как ErrorState
, ERROR_LINE () AS ErrorLine
, ERROR_PROCEDURE () как ErrorProcedure
, ERROR_MESSAGE () AS ErrorMessage;
ИДТИ
- SET XACT_ABORT ON приведет к тому, что транзакция будет нефиксируемой
- при нарушении ограничения.УСТАНОВИТЬ XACT_ABORT ON;
НАЧАТЬ ПОПРОБОВАТЬ
НАЧАТЬ СДЕЛКУ;
- Для этой таблицы существует ограничение FOREIGN KEY. Этот
- оператор вызовет ошибку нарушения ограничения.
УДАЛИТЬ ИЗ ПРОИЗВОДСТВА.
ГДЕ ProductID = 980;
- Если оператор DELETE завершается успешно, транзакция фиксируется.
ЗАВЕРШИТЬ СДЕЛКУ;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
- Выполнить процедуру поиска ошибок.
ВЫПОЛНИТЬ usp_GetErrorInfo;
- Тест XACT_STATE:
- Если 1, транзакция фиксируется.- Если -1, транзакция не фиксируется и должна
- откатиться.
- XACT_STATE = 0 означает, что транзакции нет и
- операция фиксации или отката приведет к ошибке.
- Проверить, является ли транзакция незафиксированной.
ЕСЛИ (XACT_STATE ()) = -1
НАЧИНАТЬ
РАСПЕЧАТАТЬ
N 'Транзакция находится в нефиксируемом состоянии.' +
«Откат транзакции».
ОТКАТНАЯ СДЕЛКА;
КОНЕЦ;
- Проверить, можно ли зафиксировать транзакцию.- Вы можете зафиксировать транзакцию в блоке catch, если хотите зафиксировать изменения в операторах, которые выполнялись до ошибки.
ЕСЛИ (XACT_STATE ()) = 1
НАЧИНАТЬ
РАСПЕЧАТАТЬ
N 'Транзакция подлежит фиксации.' +
«Совершение транзакции».
ЗАВЕРШИТЬ СДЕЛКУ;
КОНЕЦ;
КОНЕЦ ЗАХВАТ;
ИДТИ
Примеры: Azure Synapse Analytics и хранилище параллельных данных
D. Использование TRY … CATCH
В следующем примере показан оператор SELECT
, который генерирует ошибку деления на ноль.Ошибка вызывает переход к соответствующему блоку CATCH
.
НАЧАТЬ ПОПРОБОВАТЬ
- Сгенерировать ошибку деления на ноль.
ВЫБРАТЬ 1/0;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки
, ERROR_SEVERITY () AS Уровень серьезности ошибки
, 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)
ERROR_SEVERITY (Transact-SQL)
Transact-SQL)
RAISERROR (Transact-SQL)
@@ ERROR (Transact-SQL)
GOTO (Transact-SQL)
BEGIN…END (Transact-SQL)
XACT_STATE (Transact-SQL)
SET XACT_ABORT (Transact-SQL)
ERROR_MESSAGE (Transact-SQL) — SQL Server
- 2 минуты на чтение
В этой статье
Применимо к: SQL Server (все поддерживаемые версии) База данных SQL AzureAzure SQL Managed InstanceAzure Synapse Analytics Хранилище параллельных данных
Эта функция возвращает текст сообщения об ошибке, вызвавшей блок CATCH в TRY.Конструкция ..CATCH для выполнения.
Соглашения о синтаксисе Transact-SQL
Синтаксис
ERROR_MESSAGE ()
Типы возврата
nvarchar (4000)
Возвращаемое значение
При вызове в блоке CATCH, ERROR_MESSAGE
возвращает полный текст сообщения об ошибке, вызвавшего запуск блока CATCH
. Текст включает значения, предоставленные для любых заменяемых параметров, например, длины, имен объектов или времени.
ERROR_MESSAGE
возвращает NULL при вызове вне области действия блока CATCH.
Замечания
ERROR_MESSAGE
поддерживает вызовы в любом месте в пределах блока CATCH.
ERROR_MESSAGE
возвращает соответствующее сообщение об ошибке независимо от того, сколько раз оно выполняется или где оно выполняется в рамках блока CATCH
. Это контрастирует с такой функцией, как @@ ERROR, которая возвращает только номер ошибки в операторе, следующем сразу за тем, который вызывает ошибку.
Во вложенных блоках CATCH
, ERROR_MESSAGE
возвращает сообщение об ошибке, специфичное для области действия блока CATCH
, который ссылается на этот блок CATCH
. Например, блок CATCH
внешней конструкции TRY … CATCH может иметь внутреннюю конструкцию TRY ... CATCH
. Внутри этого внутреннего блока CATCH
ERROR_MESSAGE
возвращает сообщение об ошибке, вызвавшей внутренний блок CATCH
.Если ERROR_MESSAGE
выполняется во внешнем блоке CATCH
, он возвращает сообщение об ошибке, вызвавшей этот внешний блок CATCH
.
Примеры
A. Использование ERROR_MESSAGE в блоке CATCH
В этом примере показан оператор SELECT
, который генерирует ошибку деления на ноль. Блок CATCH
возвращает сообщение об ошибке.
НАЧАТЬ ПОПРОБОВАТЬ
- Сгенерировать ошибку деления на ноль.
ВЫБРАТЬ 1/0;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ ERROR_MESSAGE () AS ErrorMessage;
КОНЕЦ ЗАХВАТ;
ИДТИ
Вот результат.
-----------
(Затронуты 0 строк)
Сообщение об ошибке
----------------------------------
Разделить на ноль обнаружена ошибка.
(Затронута 1 строка (и))
B. Использование ERROR_MESSAGE в блоке CATCH с другими инструментами обработки ошибок
В этом примере показан оператор SELECT
, который генерирует ошибку деления на ноль. Наряду с сообщением об ошибке блок CATCH
возвращает информацию об этой ошибке.
НАЧАТЬ ПОПРОБОВАТЬ
- Сгенерировать ошибку деления на ноль.ВЫБРАТЬ 1/0;
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки
, ERROR_SEVERITY () AS Уровень серьезности ошибки
, ERROR_STATE () как ErrorState
, ERROR_PROCEDURE () как ErrorProcedure
, ERROR_LINE () AS ErrorLine
, ERROR_MESSAGE () AS ErrorMessage;
КОНЕЦ ЗАХВАТ;
ИДТИ
Вот результат.
-----------
(Затронуты 0 строк)
ErrorNumber ErrorSeverity ErrorState ErrorProcedure ErrorLine ErrorMessage
----------- ------------- ----------- --------------- ---------- ----------------------------------
8134 16 1 NULL 4 Обнаружена ошибка деления на ноль.(Затронута 1 строка (и))
См. Также
sys.messages (Transact-SQL)
TRY … CATCH (Transact-SQL)
ERROR_LINE (Transact-SQL)
ERROR_MESSAGE (Transact-SQL)
ERROR_PROCEDURE (Transact-SQL)
ERROR_SEVERITY (Transact-SQL)
ERROR_STATE (Transact-SQL)
RAISERROR (Transact-SQL)
@@ ERROR (Transact-SQL)
Справочник по ошибкам и событиям (компонент Database Engine)
sql server — Как добавить Try / Catch к хранимой процедуре SQL
TRY
/ CATCH
Обработка ошибок может происходить как внутри, так и вне процедуры (или и того, и другого).Приведенные ниже примеры демонстрируют обработку ошибок в обоих случаях.
Если вы хотите продолжить эксперименты, вы можете выполнить ответвление запроса в Stack Exchange Data Explorer.
(Здесь используется временная хранимая процедура … мы не можем создать обычных SP на SEDE, но функциональность такая же.)
- наша хранимая процедура
создать процедуру #myProc как - мы можем создавать только # временные хранимые процедуры в 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 - <- генерировать еще одну ошибку «Делить на ноль».
- раскомментируйте строку выше, чтобы вызвать ошибку в ГЛАВНОЙ процедуре ²
print 'Теперь наш ГЛАВНЫЙ блок кода sql продолжается.'
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
print 'Это блок CATCH для нашего ГЛАВНОГО блока кода sql:'
+ 'Строка ошибки #' + convert (varchar, ERROR_LINE ())
+ 'процедуры' + isnull (ERROR_PROCEDURE (), '(Main)')
КОНЕЦ ЗАХВАТ
Вот результат выполнения вышеуказанного sql как есть:
Это наша ГЛАВНАЯ процедура.Это наша хранимая процедура.
Это блок CATCH в нашей хранимой процедуре: строка ошибки № 5 процедуры #myProc
Теперь наш ГЛАВНЫЙ блок кода sql продолжается.
¹ Раскомментирование «дополнительной строки ошибки» из блока CATCH хранимой процедуры даст:
Это наша ГЛАВНАЯ процедура.
Это наша хранимая процедура.
Это блок CATCH в нашей хранимой процедуре: строка ошибки № 5 процедуры #myProc
Это блок CATCH для нашего ГЛАВНОГО блока кода sql: строка ошибки № 13 процедуры #myProc
² Если раскомментировать «дополнительную строку ошибки» процедуры MAIN , получится:
Это наша ГЛАВНАЯ процедура.Это наша хранимая процедура.
Это блок CATCH в нашей хранимой процедуре: строка ошибки № 5 процедуры #myProc
Это блок CATCH для нашего ГЛАВНОГО блока кода sql: строка ошибки № 4 процедуры (Main)
Что касается хранимых процедур и обработки ошибок, может быть полезно (и аккуратнее) использовать одну динамическую хранимую процедуру для обработки ошибок для нескольких других процедур или разделов кода.
Вот пример:
- наша процедура обработки ошибок
создать процедуру #myErrorHandling как
начинать
print 'Error #' + convert (varchar, ERROR_NUMBER ()) + ':' + ERROR_MESSAGE ()
print 'произошло в строке #' + convert (varchar, ERROR_LINE ())
+ 'процедуры' + isnull (ERROR_PROCEDURE (), '(Main)')
если ERROR_PROCEDURE () имеет значение null - проверьте, была ли ошибка в ГЛАВНОЙ процедуре
print '* Выполнение не может продолжаться после ошибки в ГЛАВНОЙ процедуре.'
конец
идти
создать процедуру #myProc как - нашу тестовую хранимую процедуру
начинать
НАЧАТЬ ПОПРОБОВАТЬ
print 'Это наша хранимая процедура.'
print 1/0 - генерирует ошибку «Делить на ноль».
print 'Мы не дойдем до этой строки.'
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
выполнить #myErrorHandling
КОНЕЦ ЗАХВАТ
конец
идти
НАЧИНАЙТЕ ПОПРОБУЙТЕ - ВАШУ ГЛАВНУЮ процедуру
print 'Это наша ГЛАВНАЯ процедура.'
выполнить #myProc - выполнить хранимую процедуру
print '* Ошибка остановила процедуру, но наш ГЛАВНЫЙ код может продолжаться.'
print 1/0 - сгенерирует еще одну ошибку «Делить на ноль».
print 'Мы не дойдем до этой строки.'
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
выполнить #myErrorHandling
КОНЕЦ ЗАХВАТ
Пример вывода : (Этот запрос можно разветвить на SEDE здесь.)
Это наша ГЛАВНАЯ процедура.
Это наша хранимая процедура.
Ошибка № 8134: Обнаружена ошибка деления на ноль.
произошло в строке # 5 процедуры #myProc
* Ошибка остановила процедуру, но наш ГЛАВНЫЙ код можно продолжить.Ошибка № 8134: Обнаружена ошибка деления на ноль.
произошло в строке # 5 процедуры (Main)
* Выполнение не может продолжаться после ошибки в ГЛАВНОЙ процедуре.
В рамках блока TRY
/ CATCH
для получения информации об ошибке, которая вызвала выполнение блока CATCH
, могут использоваться следующие системные функции:
-
ERROR_NUMBER ()
возвращает номер ошибки. -
ERROR_SEVERITY ()
возвращает серьезность. -
ERROR_STATE ()
возвращает номер состояния ошибки. -
ERROR_PROCEDURE ()
возвращает имя хранимой процедуры или триггера, в котором произошла ошибка. -
ERROR_LINE ()
возвращает номер строки внутри подпрограммы, вызвавшей ошибку. -
ERROR_MESSAGE ()
возвращает полный текст сообщения об ошибке. Текст включает значения, предоставленные для любых заменяемых параметров, таких как длина, имена объектов или время.
(Источник)
Обратите внимание, что существует два типа ошибок SQL: Terminal и Catchable . TRY
/ CATCH
[очевидно] будет ловить только ошибки "Catchable". Это один из многих способов узнать больше о своих ошибках SQL, но, вероятно, он самый полезный.
«Лучше потерпеть неудачу сейчас» (во время разработки), чем позже, потому что , как говорит Гомер, . . .
sql server - TSQL Try / Catch в транзакции или наоборот?
Открывайте транзакцию только тогда, когда вы находитесь внутри блока TRY
и непосредственно перед фактическим оператором, и сразу же фиксируйте ее.Не ждите, пока ваш контроль перейдет в конец пакета, чтобы зафиксировать ваши транзакции.
Если что-то пойдет не так, пока вы находитесь в блоке TRY
и открыли транзакцию, управление перейдет к блоку CATCH
. Просто откатите свою транзакцию и при необходимости выполните другую обработку ошибок.
Я добавил небольшую проверку для любой открытой транзакции с помощью функции @@ TRANCOUNT
перед фактическим откатом транзакции. В этом сценарии это не имеет особого смысла.Это более полезно, когда вы выполняете некоторые проверки в своем блоке TRY
, прежде чем открывать транзакцию, например, проверять значения параметров и другие вещи и вызывать ошибку в блоке TRY
, если какая-либо из проверок не удалась. В этом случае управление перейдет к блоку CATCH
, даже не открывая транзакцию. Там вы можете проверить наличие открытых транзакций и выполнить откат, если есть открытые. В вашем случае вам действительно не нужно проверять открытую транзакцию, поскольку вы не войдете в блок CATCH
, если внутри транзакции что-то не пойдет не так.
Не спрашивайте после выполнения операции DELETE
, нужно ли ее зафиксировать или откатить; сделайте все эти проверки перед открытием транзакции. Как только транзакция открыта, немедленно зафиксируйте ее, а в случае каких-либо ошибок выполните обработку ошибок (вы хорошо справляетесь, получая подробную информацию, используя почти все функции ошибок).
НАЧАТЬ ПОПРОБОВАТЬ
НАЧАТЬ СДЕЛКУ ПО РАСПИСАНИЮ СДЕЛКИ
DELETE - удалить команды полностью вырезаны из SQL
DELETE - удалить команды полностью вырезаны из SQL
DELETE - удалить команды полностью вырезаны из SQL
ЗАВЕРШИТЬ СДЕЛКУ ГРАФИК РАССЫЛКИ
PRINT 'X строк удалены.Операция «Успешная Тара». - расчет отключен.
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ЕСЛИ (@@ TRANCOUNT> 0)
НАЧИНАТЬ
РАСПИСАНИЕ ОТКАТНЫХ СДЕЛОК
ПЕЧАТЬ 'Ошибка обнаружена, все изменения отменены'
КОНЕЦ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки,
ERROR_SEVERITY () AS Уровень серьезности ошибок,
ERROR_STATE () как ErrorState,
ERROR_PROCEDURE () как ErrorProcedure,
ERROR_LINE () как ErrorLine,
ERROR_MESSAGE () как сообщение об ошибке
КОНЕЦ ЗАХВАТ
Сервер
sql - SQL Try Catch в хранимой процедуре - правильно ли я делаю?
Из Здесь
Конструкция TRY… CATCH не может охватывать несколько пакетов.ПОПЫТАТЬСЯ… ЛОВИТЬ
конструкция не может охватывать несколько блоков инструкций Transact-SQL. За
Например, конструкция TRY… CATCH не может охватывать два блока BEGIN… END
Операторы Transact-SQL и не могут охватывать конструкцию IF… ELSE.
Это означает, что вложенный блок Try ... Catch
не влияет на внешний код блока Try..Catch
, если Ошибка
возникает во внутреннем блоке.
Здесь вы хотите, чтобы мы получили результат одного запроса к другому, так что вам лучше использовать один Попробуйте..Catch
Блок
Причина
Если в коде, заключенном в блок TRY, ошибок нет,
когда завершится выполнение последнего оператора в блоке TRY, управление
переходит к оператору сразу после связанного END CATCH
утверждение. Если есть ошибка в коде, заключенном в TRY
блок, управление переходит к первому оператору в связанном CATCH
блок . Если оператор END CATCH является последним оператором в сохраненном
процедуры или триггера, управление возвращается оператору, который
вызвал хранимую процедуру или активировал триггер.
Итак, в этом случае нет лишнего страха выполнения после появления первой ошибки.
Пример:
Использование вложенного Попробуйте ... Поймать
НАЧАТЬ попробовать
объявить @ param1 как int
НАЧАТЬ Попробовать
установить @ param1 = 'gkjk'
выберите @ param1
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки,
ERROR_SEVERITY () AS Уровень серьезности ошибок,
ERROR_STATE () как ErrorState,
ERROR_PROCEDURE () как ErrorProcedure,
ERROR_LINE () как ErrorLine,
ERROR_MESSAGE () как ErrorMessage;
КОНЕЦ ЗАХВАТ
НАЧАТЬ ПОПРОБОВАТЬ
выберите "привет", "я вхожу", 200
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки,
ERROR_SEVERITY () AS Уровень серьезности ошибок,
ERROR_STATE () как ErrorState,
ERROR_PROCEDURE () как ErrorProcedure,
ERROR_LINE () как ErrorLine,
ERROR_MESSAGE () как ErrorMessage;
КОНЕЦ ЗАХВАТ
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки,
ERROR_SEVERITY () AS Уровень серьезности ошибок,
ERROR_STATE () как ErrorState,
ERROR_PROCEDURE () как ErrorProcedure,
ERROR_LINE () как ErrorLine,
ERROR_MESSAGE () как ErrorMessage;
КОНЕЦ ЗАХВАТ
Он даст вам два набора результатов.
Использование одиночного Попробовать ... Поймать
НАЧАТЬ ПОПРОБОВАТЬ
объявить @ param1 как int
установить @ param1 = 'gkjk'
выберите @ param1
выберите "привет", "я вхожу", 200
КОНЕЦ ПОПЫТКИ
НАЧАТЬ ЛОВ
ВЫБРАТЬ
ERROR_NUMBER () как номер ошибки,
ERROR_SEVERITY () AS Уровень серьезности ошибок,
ERROR_STATE () как ErrorState,
ERROR_PROCEDURE () как ErrorProcedure,
ERROR_LINE () как ErrorLine,
ERROR_MESSAGE () как ErrorMessage;
КОНЕЦ ЗАХВАТ
Где это даст вам единственный результат.
SQL TRY CATCH
SQL Try Catch помогает эффективно обрабатывать ошибки в запросе. Подобно обработке исключений в Java или C #, SQL предоставляет нам конструкцию TRY CATCH.
Например, мы пишем серию операторов внутри блока TRY. Если SQL Server обнаруживает ошибку, он выходит из блока TRY и входит в блок CATCH, он выполняет инструкции внутри блока CATCH. И, наконец, он вернет соответствующее описание ошибки.
Ниже приведен список вещей, которые следует помнить перед началом работы с SQL. Попробуйте поймать
.
- Блок CATCH должен следовать сразу за каждым блоком TRY. Вам не разрешается включать какие-либо операторы между END TRY и BEGIN CATCH
- . Если в блоке TRY нет ошибок, элемент управления не войдет в блок CATCH. Это означает, что контроллер будет выполнять операторы после END CATCH.
- Если есть ошибка в блоке TRY, он немедленно выходит из блока Try и входит в блок Catch.
- SQL TRY CATCH перехватит все ошибки, серьезность которых выше 10 и ниже 20.
- SQL позволяет использовать вложенные блоки TRY (TRY CATCH внутри другого).
- Однако блок CATCH не может обрабатывать ошибки компиляции, такие как синтаксические ошибки
SQL TRY CATCH Syntax
Синтаксис SQL Server Try Catch:
.
НАЧАТЬ ПОПРОБОВАТЬ - SQL-запросы; КОНЕЦ ПОПЫТКИ НАЧАТЬ ЛОВ - Заявления SQL; КОНЦЕВОЙ ЗАХВАТ
В блоке SQL Catch используйте следующую системную функцию для получения информации об ошибке.
SQL TRY CATCH, пример 1
В этом примере Try Catch мы найдем результат 10/0.
- Пример блока TRY CATCH в SQL Server НАЧАТЬ ПОПРОБОВАТЬ ВЫБЕРИТЕ 10/0 как результат; КОНЕЦ ПОПЫТКИ НАЧАТЬ ЛОВ SELECT ERROR_MESSAGE () AS [сообщение об ошибке] , ERROR_LINE () AS ErrorLine , ERROR_NUMBER () AS [номер ошибки] , ERROR_SEVERITY () AS [серьезность ошибки] , ERROR_STATE () AS [состояние ошибки] КОНЦЕВОЙ ЗАХВАТ
В блоке SQL TRY мы находим результат 10/0.Как мы все знаем, все, что делится на ноль, является ошибкой. Как только он обнаруживает ошибку, он переходит в блок CATCH.
ВЫБРАТЬ 10/0 КАК Результат;
Этот запрос на перехват попыток SQL вернет состояние, серьезность, номер ошибки, строку, в которой произошла ошибка, и сообщение, объясняющее ошибку.
SELECT ERROR_MESSAGE () AS [Сообщение об ошибке] , ERROR_LINE () AS ErrorLine , ERROR_NUMBER () AS [номер ошибки] , ERROR_SEVERITY () AS [серьезность ошибки] , ERROR_STATE () AS [состояние ошибки]
TRY CATCH, пример 2
В этом примере SQL Try catch мы показываем, как на самом деле будет работать try catch.Здесь мы используем различные операторы печати для отображения начала и конца как SQL TRY, так и блока Catch.
- Пример SQL Server TRY CATCH ОБЪЯВЛЯЕМ @Number TINYINT, @Result TINYINT НАЧАТЬ ПОПРОБОВАТЬ ПЕЧАТЬ N: «Это сообщение отправлено с начала БЛОКА ПОПЫТКИ» НАБОР @Number = 254; ПЕЧАТЬ N'Значение, сохраненное в @Number Variable = '+ CAST (@Number AS VARCHAR) НАБОР @Result = @Number + 1; ПЕЧАТЬ N: «Это сообщение с конца БЛОКА ПРОБЫ» ВЫБРАТЬ @Number AS Number, @Result AS Result; КОНЕЦ ПОПЫТКИ НАЧАТЬ ЛОВ ПЕЧАТЬ N: «Это сообщение отправлено с начала CATCH BLOCK» ПЕЧАТЬ N'Error Message = '+ ERROR_MESSAGE () ПЕЧАТЬ N'Error Number = '+ CAST (ERROR_NUMBER () AS VARCHAR) ПЕЧАТЬ N'Error Line = '+ CAST (ERROR_LINE () AS VARCHAR) ПЕЧАТЬ N: «Это сообщение с конца БЛОКА ЗАХВАТА» КОНЦЕВОЙ ЗАХВАТ
Позвольте мне проверить вкладку «Сообщение»
Из приведенного выше снимка экрана видно, что он печатает только операторы из блока SQL TRY.
SQL TRY CATCH, пример 3
Как использовать try catch для обработки ошибок. Это тот же код, что и в предыдущем примере, но мы изменили @Number с 254 на 255
.
- Пример SQL Server TRY CATCH ОБЪЯВЛЯЕМ @Number TINYINT, @Result TINYINT НАЧАТЬ ПОПРОБОВАТЬ ПЕЧАТЬ N: «Это сообщение отправлено с начала БЛОКА ПОПЫТКИ» НАБОР @Number = 255; ПЕЧАТЬ N'Значение, сохраненное в @Number Variable = '+ CAST (@Number AS VARCHAR) НАБОР @Result = @Number + 1; ПЕЧАТЬ N: «Это сообщение с конца БЛОКА ПРОБЫ» ВЫБРАТЬ @Number AS Number, @Result AS Result; КОНЕЦ ПОПЫТКИ НАЧАТЬ ЛОВ ПЕЧАТЬ N: «Это сообщение отправлено с начала CATCH BLOCK» ПЕЧАТЬ N'Error Message = '+ ERROR_MESSAGE () ПЕЧАТЬ N'Error Number = '+ CAST (ERROR_NUMBER () AS VARCHAR) ПЕЧАТЬ N'Error Line = '+ CAST (ERROR_LINE () AS VARCHAR) ПЕЧАТЬ N: «Это сообщение с конца БЛОКА ЗАХВАТА» КОНЦЕВОЙ ЗАХВАТ
АНАЛИЗ
В этом примере SQL try catch сначала мы объявили две переменные tinyint
ЗАЯВИТЬ @Number TINYINT, @ Результат TINYINT
Затем в блоке TRY мы присвоили 255 числовой переменной и выполняем сложение для переменной результата.
ПЕЧАТЬ N 'Это сообщение отправлено с начала БЛОКА ПЕРЕДАЧИ' НАБОР @Number = 255; ПЕЧАТЬ N'Значение, сохраненное в @Number Variable = '+ CAST (@Number AS VARCHAR) НАБОР @Result = @Number + 1; ПЕЧАТЬ N 'Это сообщение находится с конца БЛОКА ПРОБЫ'
Как мы все знаем, крошечный int содержит до 255 означает переполнение. Итак, он выйдет из блока TRY и выполнит оператор внутри нашего блока CATCH, который:
ПЕЧАТЬ N 'Это сообщение отправлено с начала БЛОКА ЗАХВАТА' ПЕЧАТЬ N'Error Message = '+ ERROR_MESSAGE () ПЕЧАТЬ N'Error Number = '+ CAST (ERROR_NUMBER () AS VARCHAR) ПЕЧАТЬ N'Error Line = '+ CAST (ERROR_LINE () AS VARCHAR) ПЕЧАТЬ N 'Это сообщение с конца CATCH BLOCK'
Если вы посмотрите на приведенный выше снимок экрана с примером Try catch, хотя у нас есть оператор печати в конце блока TRY, контроллер пропустил этот оператор.Это потому, что, как только он входит в SET @Result = @Number + 1; Элемент управления выйдет из блока TRY и сразу войдет в блок Catch.
Использование Try-Catch для обработки ошибок
- Автор Лори Браун @SQLSupahStah
Хотя Try-Catch существует с SQL 2005, вы удивитесь, насколько редко приложения и программисты SQL используют его для обработки ошибок. Поскольку я также пишу свой собственный код обслуживания и мониторинга и задания, которые должны быть развернуты для моих клиентов, я начал прилагать усилия для обработки ошибок, особенно в сценариях реализации и в создаваемых мною заданиях.
Вот простой пример блока Try-Catch:
НАЧАТЬ ПОПРОБОВАТЬ
[Некоторая инструкция SQL]
КОНЕЦ ПОПЫТЫ
НАЧАТЬ ПОИСК
[Обработка ошибок или другой оператор SQL]
КОНЦЕВОЙ ЗАЖИМ
Обычно блок Try помещается вокруг оператора, для которого вы хотите записать информацию об ошибке, а блок Catch помещается вокруг оператора, который определяет, что вы хотите выполнить в случае ошибки. Довольно просто! 🙂
Есть некоторые правила….За блоком Try должен сразу же следовать блок Catch, и он улавливает только ошибки с уровнем серьезности 10 или выше, поэтому он улавливает множество вещей. Вы можете использовать их в множестве мест (триггеры, задания, хранимые процедуры и многие другие), и вы можете вкладывать блоки Try-Catch, но убедитесь, что вы знаете, куда выйдет блок, если он обнаружит ошибку. Он не будет обнаруживать ошибки с уровнем серьезности 20 и выше, ошибки компиляции или что-либо, что нарушает или разрывает соединение (например, оператор KILL).
Существуют удобные системные функции, которые можно использовать для получения информации об ошибке в блоке Catch.
- ERROR_NUMBER () возвращает номер ошибки.
- ERROR_SEVERITY () возвращает серьезность.
- ERROR_STATE () возвращает номер состояния ошибки.
- ERROR_PROCEDURE () возвращает имя хранимой процедуры или триггера, в котором произошла ошибка.
- ERROR_LINE () возвращает номер строки внутри процедуры, вызвавшей ошибку.
- ERROR_MESSAGE () возвращает полный текст сообщения об ошибке. Текст включает значения, предоставленные для любых заменяемых параметров, таких как длина, имена объектов или время.
Я использую блок Try-Catch в задании, которое используется для создания отчета. Поскольку создание отчета выполняется с использованием среды CLR, в случае сбоя он может дать некоторую неинформативную информацию о причинах, что всегда затрудняет устранение неполадок. Я также хотел фиксировать информацию о запуске для своих заданий по отчетности и создал таблицу, предназначенную для хранения этой информации вместе с сообщениями об ошибках, фиксируемыми блоками Catch в случае их возникновения.
- создать таблицу ReportExecInfo
СОЗДАТЬ ТАБЛИЦУ ReportExecInfo (
ReportName VARCHAR (100) NOT NULL,
Last_Run DATETIME NULL,
Ошибка VARCHAR (1) NULL,
Сообщение об ошибке VARCHAR (100) NULL
) НА [ПЕРВИЧНОМ]
ГО
СОЗДАТЬ КЛАСТЕРИРОВАННЫЙ ИНДЕКС [PK_ReportExecInfo] НА [dbo].[ReportExecInfo]
(
ReportName ASC
) С (FILLFACTOR = 90) НА [ПЕРВИЧНОМ]
ГО
- Вставить имена отчетов
ВСТАВИТЬ ЗНАЧЕНИЯ ReportExecInfo (ReportName) («DeadlockReport»)
ВСТАВИТЬ ЗНАЧЕНИЯ ReportExecInfo (ReportName) («BlockingReport»)
ВСТАВИТЬ ЗНАЧЕНИЯ ReportExecInfo (ReportName) («WeeklyHealthReport»)
GO
Затем приведенный ниже код помещается в задание, которое будет регистрировать успешное или неудачное создание отчета.Я использую функцию ERROR_MESSAGE (), чтобы получить сообщение об ошибке, если оно сгенерировано.
ОБЪЯВИТЬ @rptname VARCHAR (200)
ОБЪЯВИТЬ @rptpath VARCHAR (200)
ОБЪЯВИТЬ @attchpath VARCHAR (500)
ОБЪЯВИТЬ @dtstmp VARCHAR (50)
ОБЪЯВИТЬ @ServerName VARCHAR (20)
ОБЪЯВИТЬ @NumRows INT
ОБЪЯВИТЬ @NumDays INT
ОБЪЯВИТЬ @cmd VARCHAR (500)
НАБОР @NumDays = 7
SET @rptpath = ‘M: \ Reports’
НАБОР @ServerName = @@ SERVERNAME
SET @dtstmp = CAST (DATEPART (yyyy, GETDATE ()) AS VARCHAR (4)) + CAST (DATEPART (mm, GETDATE ()) AS VARCHAR (2)) + CAST (DATEPART (dd, GETDATE ()) AS VARCHAR (2))
НАБОР @rptname = @ServerName + ‘- WeeklyHealthReport -‘ + @ dtstmp
SET @attchpath = @rptpath + ‘\’ + @ rptname + ‘.