TRY...CATCH (Transact-SQL)

适用于: SQL Server Azure SQL 数据库 Azure SQL 托管实例 Azure Synapse Analytics 分析平台系统 (PDW) Microsoft Fabric 中的 SQL 分析端点 Microsoft Fabric 中的仓库

为 Transact-SQL 实现与 C# 和 Visual C++ 语言中的异常处理类似的错误处理。 一组 Transact-SQL 语句可以包含在块 TRY 中。 如果块中 TRY 发生错误,控件通常传递给包含在块中的另一 CATCH 组语句。

Transact-SQL 语法约定

语法

BEGIN TRY
    { sql_statement | statement_block }
END TRY
BEGIN CATCH
    [ { sql_statement | statement_block } ]
END CATCH
[ ; ]

参数

sql_statement

任何 Transact-SQL 语句。

statement_block

批处理中的任何一组 Transact-SQL 语句或包含在块 BEGIN...END 中。

注解

TRY...CATCH构造捕获严重性高于 10 的所有执行错误,这些错误不会关闭数据库连接。

TRY 必须紧跟关联 CATCH 块。 包括语句BEGIN CATCH之间的END TRY任何其他语句将生成语法错误。

构造 TRY...CATCH 不能跨越多个批处理。 构造 TRY...CATCH 不能跨越 Transact-SQL 语句的多个块。 例如,构造不能跨越 Transact-SQL 语句的两BEGIN...ENDTRY...CATCH块,并且不能跨越构造IF...ELSE

如果代码中没有包含在块中的错误,则当块中的TRY最后一个TRY语句完成时,控件会在关联END CATCH语句之后立即传递给该语句。

如果代码中有一个包含在块中的 TRY 错误,则控件将传递给关联 CATCH 块中的第一个语句。 当块中的 CATCH 代码完成时,控件会紧接在语句之后 END CATCH 传递给该语句。

注意

END CATCH如果该语句是存储过程或触发器中的最后一个语句,则控制权将传回调用存储过程或触发触发器的语句。

被块捕获 CATCH 的错误不会返回到调用应用程序。 如果错误信息的任何部分必须返回到应用程序,则块中的CATCH代码必须通过使用结果集或RAISERROR语句PRINTSELECT机制执行此操作。

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 语句不能用于输入 TRYCATCH 块。 GOTO语句可用于跳转到相同TRYCATCH块内的标签或离开或TRYCATCH块。

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