在数据库脚本编写过程中,错误处理是一个不可忽视的重要环节。本文将聚焦于SQL Server中的错误处理机制,探讨错误消息的构成、SQL Server对错误的不同反应,以及如何通过XACT_ABORT选项控制事务的行为。在后续的文章中,将提供用于存储过程和函数中错误管理的模板。
错误消息由几个关键部分组成,包括错误编号、严重性级别、错误状态、引发错误的存储过程、错误行号以及错误消息本身。
每个错误都有一个唯一的编号。系统定义的错误编号可以从Master数据库的sysmessages表中找到。默认情况下,RAISERROR使用的错误消息编号为50000。可以通过系统存储过程sp_addmessage来添加自定义错误消息。
-- 添加自定义错误消息
EXEC sp_addmessage @msgnum = 50003, @severity = 11, @msgtext = 'My Own Error';
同样,可以使用sp_dropmessage来移除自定义的错误消息。
-- 移除自定义错误消息
EXEC sp_dropmessage @msgnum = 50003;
严重性级别是一个介于1到25之间的数字。0-10表示警告或信息性错误,11-16表示编程错误,17-25表示资源/硬件/操作系统/SQL Server内部错误。严重性级别较高的错误消息被认为是致命的。
错误状态是一个整数值。如果在代码的多个点上引发相同的错误,可以使用错误状态来区分引发错误的脚本部分。
-- 示例:使用错误状态区分错误
IF
RAISERROR (N'Error raised for condition1', 16, 0);
IF
RAISERROR (N'Error raised for condition2', 16, 1);
当错误发生时,SQL Server可能会采取四种主要的行动。
SQL Server终止引发错误的语句,但继续执行下一条语句。如果存在未回滚的打开事务,则事务不受影响。
-- 示例:语句终止
PRINT 'Beginning of execution';
INSERT INTO Book(BookID, Name, Price) VALUES (1, 'Demo Book1', 100);
INSERT INTO Book(BookID, Name, Price) VALUES (1, 'Demo Book2', 200); -- 这里会引发错误
PRINT 'End of execution';
SQL Server终止当前作用域中的所有语句,并继续执行引发错误的作用域之外的所有语句。当前作用域指的是存储过程、用户定义的函数或SQL语句块,包括动态SQL。
-- 示例:作用域终止
PRINT 'Beginning of Scope execution';
EXEC('SELECT BookID FROM BOOKs'); -- 动态执行SELECT查询
PRINT 'End of Scope execution';
整个客户端调用的执行被终止。如果存在任何打开的事务,这些事务将被回滚。这主要发生在大多数转换错误上,例如尝试将非数字字符串转换为数字、死锁问题、执行存储过程时参数数量不匹配或向表中插入数据时。
-- 示例:批处理终止
PRINT 'Beginning of Batch execution';
SELECT BookID FROM BOOKs; -- 尝试从不存在的表中选择数据
PRINT 'End of Batch execution';
对于严重性级别为20-25的错误,此错误操作会导致客户端断开连接,任何打开的事务都将被回滚。这种情况仅在SQL Server发生严重问题时发生,如硬件问题、网络问题、数据库损坏或严重资源问题。
可以设置XACT_ABORT为ON或OFF,默认为OFF。因此,SQL Server将根据错误的严重性级别决定采取什么行动。SET XACT_ABORT ON指令SQL Server在遇到错误时终止整个事务。
-- 示例:XACT_ABORT ON
CREATE PROCEDURE dbo.XactAbortONSP AS
BEGIN
SET XACT_ABORT ON
PRINT 'Beginning of execution';
EXEC MissingSP; -- 这个存储过程不存在
PRINT 'End of execution';
END