实现结构化异常处理

已完成

现在,你已了解 T-SQL 中错误的性质和基本错误处理,现在可以了解更高级的错误处理形式。 SQL Server 2005 中引入了结构化异常处理。

在这里,你将了解如何使用它并评估其优点和限制,包括 TRY CATCH 块、错误处理函数的角色,以及了解可捕获和不可捕获的错误之间的差异。 最后,你将了解在必要时如何管理和呈现错误。

什么是 TRY/CATCH 块编程

结构化异常处理比基于@@ERROR系统变量的错误处理更强大。 它可以防止错误处理代码散落在各处,并使这些代码集中统一管理。 错误处理代码的集中化也意味着你可以更多地关注代码的目的,而不是它所包含的错误处理。

TRY 块和 CATCH 块

使用结构化异常处理时,可能会引发错误的代码放置在 TRY 块中。 TRY 块由 BEGIN TRYEND TRY 语句括起来。

如果发生可捕获的错误,大多数错误都能被捕获,程序执行控制将转移到 CATCH 块。 CATCH 块是由 BEGIN CATCHEND CATCH 语句括起来的一系列 T-SQL 语句。

注释

虽然 BEGIN CATCH 和 END TRY 是单独的语句,但 BEGIN CATCH 必须紧跟在 END TRY 之后。

当前限制

高级语言通常提供 try/catch/finally 构造,通常用于隐式释放资源。 T-SQL 中没有等效的 FINALLY 块。

了解可捕获错误和非可捕获错误之间的差异

值得注意的是,虽然 TRY/CATCH 块允许你捕获比使用 @@ERROR 更广泛的错误,但你不能捕获每种类型。

可捕获与不可捕获的错误

在 TRY/CATCH 块存在的相同范围内,TRY/CATCH 块并不能捕获所有错误。 通常,不能在同一范围内捕获的错误可以在周边范围内捕获。 例如,你可能无法捕获包含 TRY/CATCH 块的存储过程中的错误。 但是,你可能会在调用了出现错误的存储过程的代码的 TRY/CATCH 块中捕获此错误。

常见的不可捕获错误

不可识别错误的常见示例包括:

  • 编译错误,例如阻止批处理编译的语法错误。
  • 语句级重新编译问题,通常与延迟名称解析有关。 例如,可以创建引用未知表的存储过程。 仅当过程尝试将表的名称解析为 objectid 时,才会引发错误。

如何使用 THROW 重新引发错误

如果在 CATCH 块中使用 THROW 语句而不带任何参数,则会重新引发导致代码进入 CATCH 块的错误。 可以使用此方法在数据库中实现错误日志记录,方法是捕获错误并记录其详细信息,然后将原始错误引发给客户端应用程序,以便可以处理该错误。

这是一个关于如何重新引发错误的示例。

BEGIN TRY
    -- code to be executed
END TRY
BEGIN CATCH
    PRINT ERROR_MESSAGE();
    THROW
END CATCH

在某些早期版本的 SQL Server 中,没有引发系统错误的方法。 尽管 THROW 无法指定要引发的系统错误,但如果在 CATCH 块中使用 THROW 时没有参数,它将重新引发系统错误和用户错误。

什么是错误处理函数

CATCH 块使与错误相关的信息在 CATCH 块的整个持续时间内可用。 这包括子范围(例如存储过程),从 CATCH 块中运行。

错误处理函数

你应该记得,使用 @@ERROR 编程时,在执行下一个语句后,@@ERROR 系统变量保留的值就会重置。

T-SQL 中结构化异常处理的另一个关键优点是提供了一系列错误处理函数,并且这些函数在整个 CATCH 块中保留其值。 单独的函数提供已引发的错误的每个属性。

这意味着可以编写一般错误处理存储过程,这些存储过程仍可访问与错误相关的信息。

  • CATCH 块使与错误相关的信息在 CATCH 块的整个持续时间内可用。
  • 运行下一个语句时,将重置@@Error。

管理代码中的错误

SQL CLR 集成允许在 SQL Server 中执行托管代码。 高级 .NET 语言(如 C# 和 VB)具有可用的详细异常处理。 可使用标准 .NET try/catch/finally 块捕获错误。

托管代码中的错误

通常,你可能希望尽可能多地捕获托管代码中的错误。 但是,请务必认识到,托管代码中未处理的任何错误都会传回调用 T-SQL 代码。 每当托管代码中发生的任何错误都返回到 SQL Server 时,它将显示为 6522 错误。 错误可以嵌套,而特定错误可能掩盖错误的真正原因。

托管代码中另一个罕见但可能的错误原因是代码可以通过 SqlCommand 对象执行 RAISERROR T-SQL 语句。