实现 T-SQL 错误处理
错误表示在数据库操作期间出现的问题或值得注意的事项。 SQL Server 数据库引擎可以生成错误,以响应系统级别的事件或故障;或者可以在 Transact-SQL 代码中生成应用程序错误。
数据库引擎错误的元素
无论原因是什么,每个错误都由以下元素组成:
- 错误号 - 标识特定错误的唯一编号。
- 错误消息 - 描述错误的文本。
- 严重性 - 从 1 到 25 的严重性的数字指示。
- State - 数据库引擎条件的内部状态代码。
- 过程 - 发生错误的存储过程或触发器的名称。
- 行号 - 批处理或过程中的哪个语句生成了错误。
系统错误
系统错误是预定义的,可以在 sys.messages 系统视图中查看它们。 发生系统错误时,SQL Server 可能会执行自动修正作,具体取决于错误的严重性。 例如,发生高严重性错误时,SQL Server 可能会使数据库脱机,甚至停止数据库引擎服务。
自定义错误
可以在 Transact-SQL 代码中生成错误,以响应应用程序特定的条件,或自定义发送到客户端应用程序的信息以响应系统错误。 可以在生成这些错误的位置内联定义这些应用程序错误,也可以在 sys.messages 表中与系统提供的错误一起预定义这些错误。 必须使用 50001 或更大的数字作为自定义错误的错误号。
若要将自定义错误消息添加到 sys.messages,请使用sp_addmessage。 消息的用户必须是 sysadmin 成员或 serveradmin 固定服务器角色。
这是sp_addmessage语法:
sp_addmessage [ @msgnum= ] msg_id , [ @severity= ] severity , [ @msgtext= ] 'msg'
[ , [ @lang= ] 'language' ]
[ , [ @with_log= ] { 'TRUE' | 'FALSE' } ]
[ , [ @replace= ] 'replace' ]
下面是使用此语法的自定义错误消息的示例:
sp_addmessage 50001, 10, N’Unexpected value entered’;
此外,还可以定义自定义错误消息, sysadmin 服务器 角色的成员还可以使用其他参数 ,@with_log。 设置为 TRUE 时,错误也会记录在 Windows 应用程序日志中。 写入 Windows 应用程序日志的任何消息也会写入 SQL Server 错误日志。 谨慎使用 @with_log 此选项,因为网络和系统管理员倾向于不喜欢系统日志中“聊天”的应用程序。 但是,如果错误需要被警报捕获,则必须首先将错误写入 Windows 应用程序日志。
注释
不支持引发系统错误。
可以使用@replace =“replace”选项替换消息,而无需先删除它们。
消息是可自定义的,可以根据language_id值为多种语言的相同错误号添加不同的消息。
注释
英语消息的语言 ID 是 1033。
使用 RAISERROR 引发错误
PRINT 和 RAISERROR 都可用于向应用程序返回信息或警告消息。 RAISERROR 允许应用程序引发错误,然后调用进程可能会捕获该错误。
RAISERROR
在 T-SQL 中抛出错误的功能使应用程序中的错误处理变得更容易,因为它会像其他系统错误一样被发送。 RAISERROR 用于:
- 帮助排查 T-SQL 代码问题。
- 检查数据的值。
- 返回包含变量文本的消息。
注释
使用 PRINT 语句类似于引发严重性为 10 的错误。
下面是使用 RAISERROR 的自定义错误消息的示例。
RAISERROR (N'%s %d', -- Message text,
10, -- Severity,
1, -- State,
N'Custom error message number',
2)
触发时,它将返回:
Custom error message number 2
在前面的示例中, %d 是数字的占位符, %s 是字符串的占位符。 此外,应注意未提及消息编号。 使用此语法引发包含消息字符串的错误时,它们始终具有错误号 50000。
使用 THROW 引发错误
THROW 语句提供了一种更简单的方法,用于在代码中引发错误。 错误的错误号必须至少为 50000。
THROW
THROW 与 RAISERROR 在几种方面不同:
- THROW 引发的错误的严重性始终为 16。
- THROW 返回的消息与 sys.sysmessages 中的任何条目无关。
- THROW 引发的错误仅在与 SET XACT_ABORT ON 一起使用时导致事务中止,并且会话将终止。
THROW 50001, 'An Error Occured',0
使用@@Error捕获错误代码
SQL Server 应用程序中的大多数传统错误处理代码都是使用 @@ERROR 创建的。 SQL Server 2005 中引入了结构化异常处理,并提供使用@@ERROR的强替代方法。 下一课将讨论它。 大量的现有 SQL Server 错误处理代码基于@@ERROR,因此请务必了解如何使用它。
@@ERROR
@@ERROR是一个系统变量,用于保存发生的最后一个错误的错误号。 @@ERROR 的一个显著挑战是,在执行每个附加语句时,其保留的值会快速重置。
例如,请考虑以下代码:
RAISERROR(N'Message', 16, 1);
IF @@ERROR <> 0
PRINT 'Error=' + CAST(@@ERROR AS VARCHAR(8));
GO
你可能预计,执行代码时,它会在打印的字符串中返回错误号。 但是,执行代码时,它将返回:
Msg 50000, Level 16, State 1, Line 1
Message
Error=0
引发错误,但输出的消息为“Error=0”。 在输出的第一行中,你可以看到,错误实际上为 50000(如预期),并且一条消息被传递给 RAISERROR。 这是因为在 RAISERROR 语句后面的 IF 语句已成功执行,导致 @@ERROR 值重置。 因此,在使用@@ERROR时,需要在错误号被引发后立即将其捕获到变量中,然后通过该变量继续处理。
查看演示以下代码:
DECLARE @ErrorValue int;
RAISERROR(N'Message', 16, 1);
SET @ErrorValue = @@ERROR;
IF @ErrorValue <> 0
PRINT 'Error=' + CAST(@ErrorValue AS VARCHAR(8));
执行此代码后,它将返回以下输出:
Msg 50000, Level 16, State 1, Line 2
Message
Error=50000
错误号现已正确报告。
集中处理错误
使用@@ERROR进行错误处理的另一个重要问题是很难在 T-SQL 代码中集中化。 错误处理往往最终分散在代码中。 通过使用标签和 GOTO 语句,可以将使用 @@ERROR 的错误处理集中到某个范围。 然而,这种做法会被大多数开发人员视为糟糕的编码习惯。
创建错误警报
对于某些类别的错误,管理员可能会创建 SQL Server 警报,因为它们希望在发生这些警报后立即收到通知。 这甚至可以应用于用户定义的错误消息。 例如,你可能希望在事务日志填满时引发警报。 警报通常用于引起管理员注意的高严重性级别的错误(如严重性级别19或更高)。
引发警报
可以为特定错误消息创建警报。 警报服务的工作原理是将自身注册为具有事件日志记录服务的回叫服务。 这意味着警报仅适用于已记录的错误。
有两种方法可以使错误引发警报,即,当引发错误时可以使用 WITH LOG 选项,也可以通过执行 sp_altermessage 将消息更改为记录到日志中。 WITH LOG 选项仅影响当前语句。 使用 sp_altermessage 会更改此错误行为,以供将来使用。 从 SQL Server 2005 SP3 或 SQL Server 2008 SP1 及更高版本开始,只能通过 sp_altermessage 修改系统错误。