TRY...CATCH (Transact-SQL)
适用于:SQL Server Azure SQL 数据库 Azure SQL 托管实例 Azure Synapse Analytics Analytics Platform System (PDW) Microsoft Fabric 中的 SQL 终结点 Microsoft Fabric 中的仓库 Microsoft Fabric SQL 数据库
为 Transact-SQL 实现与 C# 和 Visual C++ 语言中的异常处理类似的错误处理。 一组 Transact-SQL 语句可以包含在块 TRY
中。 如果块中 TRY
发生错误,控件通常传递给包含在块中的另一 CATCH
组语句。
语法
BEGIN TRY
{ sql_statement | statement_block }
END TRY
BEGIN CATCH
[ { sql_statement | statement_block } ]
END CATCH
[ ; ]
参数
sql_statement
任何 Transact-SQL 语句。
statement_block
批处理中的任何一组 Transact-SQL 语句或包含在块 BEGIN...END
中。
注解
TRY...CATCH
构造捕获严重性高于 10 的所有执行错误,这些错误不会关闭数据库连接。
块 TRY
必须紧跟关联 CATCH
块。 包括语句BEGIN CATCH
之间的END TRY
任何其他语句将生成语法错误。
构造 TRY...CATCH
不能跨越多个批处理。 构造 TRY...CATCH
不能跨越 Transact-SQL 语句的多个块。 例如,构造不能跨越 Transact-SQL 语句的两BEGIN...END
个TRY...CATCH
块,并且不能跨越构造IF...ELSE
。
如果代码中没有包含在块中的错误,则当块中的TRY
最后一个TRY
语句完成时,控件会在关联END CATCH
语句之后立即传递给该语句。
如果代码中有一个包含在块中的 TRY
错误,则控件将传递给关联 CATCH
块中的第一个语句。 当块中的 CATCH
代码完成时,控件会紧接在语句之后 END CATCH
传递给该语句。
注意
END CATCH
如果该语句是存储过程或触发器中的最后一个语句,则控制权将传回调用存储过程或触发触发器的语句。
被块捕获 CATCH
的错误不会返回到调用应用程序。 如果错误信息的任何部分必须返回到应用程序,则块中的CATCH
代码必须通过使用结果集或RAISERROR
语句PRINT
等SELECT
机制执行此操作。
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 | 返回错误消息的完整文本。 该文本包括为所有可替换参数提供的值,如长度、对象名或时间。 |
如果这些函数在块范围CATCH
之外调用,则返回NULL
这些函数。 可以使用这些函数从块范围内 CATCH
的任何位置检索错误信息。 例如,下面的脚本显示了包含错误处理函数的存储过程。 在 CATCH
构造的 TRY...CATCH
块中,调用了该存储过程并返回有关错误的信息。
-- Verify that the stored procedure does not already exist.
IF OBJECT_ID('usp_GetErrorInfo', 'P') IS NOT NULL
DROP PROCEDURE usp_GetErrorInfo;
GO
-- Create procedure to retrieve error information.
CREATE PROCEDURE usp_GetErrorInfo
AS
SELECT ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
GO
BEGIN TRY
-- Generate divide-by-zero error.
SELECT 1 / 0;
END TRY
BEGIN CATCH
-- Execute error retrieval routine.
EXECUTE usp_GetErrorInfo;
END CATCH;
这些ERROR_*
函数还可以在CATCH
本机编译的存储过程内的块中工作。
TRY 不受影响的错误...CATCH 构造
TRY...CATCH
构造不会捕获以下条件:
严重级别为 10 或更低的警告或信息性消息。
严重级别为 20 或更高且终止会话的 SQL Server 数据库引擎任务处理的错误。 如果发生严重性为 20 或更高的错误,并且数据库连接不会中断,
TRY...CATCH
请处理该错误。需要关注的消息,如客户端中断请求或客户端连接中断。
当系统管理员使用
KILL
语句结束会话时。
在与构造相同的执行TRY...CATCH
级别发生时,块不会处理CATCH
以下类型的错误:
编写错误,例如禁止运行批处理的语法错误。
语句级重新编写过程中出现的错误,例如由于名称解析延迟而造成在编写后出现对象名解析错误。
对象名解析错误
这些错误会被返回到运行批处理、存储过程或触发器的级别。
如果在编译或语句级重新编译期间在较低执行级别(例如,执行 sp_executesql
或用户定义存储过程)内在块内 TRY
发生错误,则错误发生在低于构造的级别 TRY...CATCH
,将由关联的 CATCH
块处理。
以下示例演示了构造未捕获TRY...CATCH
由语句生成的SELECT
对象名称解析错误,但在存储过程内执行同SELECT
一语句时,块会捕获CATCH
该错误。
BEGIN TRY
-- Table does not exist; object name resolution
-- error not caught.
SELECT *
FROM NonexistentTable;
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH
未捕获错误,并且控制将构造传出 TRY...CATCH
到下一个更高的级别。
在 SELECT
存储过程内运行该语句会导致错误发生在低于 TRY
块的级别。 该错误由 TRY...CATCH
构造处理。
-- Verify that the stored procedure does not exist.
IF OBJECT_ID(N'usp_ExampleProc', N'P') IS NOT NULL
DROP PROCEDURE usp_ExampleProc;
GO
-- Create a stored procedure that will cause an
-- object resolution error.
CREATE PROCEDURE usp_ExampleProc
AS
SELECT *
FROM NonexistentTable;
GO
BEGIN TRY
EXECUTE usp_ExampleProc;
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
不可提交事务和XACT_STATE
如果在块中 TRY
生成的错误导致当前事务的状态失效,则事务被归类为不可提交事务。 通常在块外部 TRY
结束事务的错误会导致事务在块内 TRY
发生错误时进入不可提交状态。 不可提交的事务只能执行读取操作或 ROLLBACK TRANSACTION
。 事务无法执行生成写入操作或 a COMMIT TRANSACTION
. 如果事务已分类为不可提交事务,该 XACT_STATE
函数将返回一个值 -1
。 当批处理结束时,数据库引擎将回滚所有不可提交的活动事务。 如果事务进入不可提交状态时未发送错误消息,则批处理完成后,会将错误消息发送到客户端应用程序。 该消息指示检测到并回滚了一个不可提交的事务。
有关不可提交事务和 XACT_STATE
函数的详细信息,请参阅 XACT_STATE。
示例
A. 使用 TRY...CATCH
下面的示例显示生成被零除错误的 SELECT
语句。 该错误会使执行跳转到关联的 CATCH
块。
BEGIN TRY
-- Generate a divide-by-zero error.
SELECT 1 / 0;
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO
B. 使用 TRY...事务中的 CATCH
以下示例显示 TRY...CATCH
块在事务内的工作方式。 TRY
块内的语句会生成违反约束的错误。
BEGIN TRANSACTION;
BEGIN TRY
-- Generate a constraint violation error.
DELETE
FROM Production.Product
WHERE ProductID = 980;
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF @@TRANCOUNT > 0
COMMIT TRANSACTION;
GO
°C 使用 TRY...CATCH 与 XACT_STATE
以下示例显示如何使用 TRY...CATCH
构造来处理事务内发生的错误。 XACT_STATE
函数确定应提交事务还是应回滚事务。 在本示例中,SET XACT_ABORT
状态为 ON
。 在发生违反约束的错误时,这会使事务处于不可提交状态。
-- Check to see whether this stored procedure exists.
IF OBJECT_ID(N'usp_GetErrorInfo', N'P') IS NOT NULL
DROP PROCEDURE usp_GetErrorInfo;
GO
-- Create procedure to retrieve error information.
CREATE PROCEDURE usp_GetErrorInfo
AS
SELECT ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_LINE() AS ErrorLine,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_MESSAGE() AS ErrorMessage;
GO
-- SET XACT_ABORT ON will cause the transaction to be uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
-- A FOREIGN KEY constraint exists on this table. This
-- statement will generate a constraint violation error.
DELETE
FROM Production.Product
WHERE ProductID = 980;
-- If the DELETE statement succeeds, commit the transaction.
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- Execute error retrieval routine.
EXECUTE usp_GetErrorInfo;
-- Test XACT_STATE:
-- If 1, the transaction is committable.
-- If -1, the transaction is uncommittable and should
-- be rolled back.
-- XACT_STATE = 0 means that there is no transaction and
-- a commit or rollback operation would generate an error.
-- Test whether the transaction is uncommittable.
IF (XACT_STATE()) = -1
BEGIN
PRINT N'The transaction is in an uncommittable state. Rolling back transaction.'
ROLLBACK TRANSACTION;
END;
-- Test whether the transaction is committable.
-- You may want to commit a transaction in a catch block if you want to commit changes to statements that ran prior to the error.
IF (XACT_STATE()) = 1
BEGIN
PRINT N'The transaction is committable. Committing transaction.'
COMMIT TRANSACTION;
END;
END CATCH;
GO
相关内容
- THROW (Transact-SQL)
- 数据库引擎错误严重性
- ERROR_LINE (Transact-SQL)
- ERROR_MESSAGE (Transact-SQL)
- ERROR_NUMBER (Transact-SQL)
- ERROR_PROCEDURE (Transact-SQL)
- ERROR_SEVERITY (Transact-SQL)
- ERROR_STATE (Transact-SQL)
- RAISERROR (Transact-SQL)
- @@ERROR (Transact-SQL)
- GOTO (Transact-SQL)
- BEGIN...END (Transact-SQL)
- XACT_STATE (Transact-SQL)
- SET XACT_ABORT (Transact-SQL)