实现结构化异常处理
现在,你已了解 T-SQL 中错误的性质和基本错误处理,现在可以了解更高级的错误处理形式。 SQL Server 2005 中引入了结构化异常处理。
在这里,你将了解如何使用它并评估其优点和限制,包括 TRY CATCH 块、错误处理函数的角色,以及了解可捕获和不可捕获的错误之间的差异。 最后,你将了解在必要时如何管理和呈现错误。
什么是 TRY/CATCH 块编程
结构化异常处理比基于@@ERROR系统变量的错误处理更强大。 它可以防止错误处理代码散落在各处,并使这些代码集中统一管理。 错误处理代码的集中化也意味着你可以更多地关注代码的目的,而不是它所包含的错误处理。
TRY 块和 CATCH 块
使用结构化异常处理时,可能会引发错误的代码放置在 TRY 块中。 TRY 块由 BEGIN TRY 和 END TRY 语句括起来。
如果发生可捕获的错误,大多数错误都能被捕获,程序执行控制将转移到 CATCH 块。 CATCH 块是由 BEGIN CATCH 和 END 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 语句。