SQL存储过程错误处理的基础认知
SQL Server的存储过程是一组预编译的SQL语句集合,常用于封装复杂业务逻辑。当存储过程执行过程中出现运行时错误时,如果没有主动处理,错误会直接返回给调用端,不仅不利于问题排查,还可能导致后续业务步骤中断。TRY和CATCH块是SQL Server提供的结构化错误处理机制,能够在存储过程内部捕获异常并做针对性处理。

TRY和CATCH块的基本语法结构
TRY块中放置可能会出现错误的SQL语句,当TRY块中的语句执行出错时,程序会立即跳转到对应的CATCH块执行错误处理逻辑。基本语法如下:
BEGIN TRY
-- 可能会出错的SQL语句
SQL语句1;
SQL语句2;
END TRY
BEGIN CATCH
-- 错误处理语句
-- 可以获取错误信息的相关函数
END CATCH
错误信息的获取函数
在CATCH块中,SQL Server提供了一系列系统函数来获取当前异常的详细信息,常用的函数如下:
ERROR_NUMBER():返回错误的编号ERROR_MESSAGE():返回错误的描述信息ERROR_SEVERITY():返回错误的严重程度ERROR_STATE():返回错误的状态号ERROR_PROCEDURE():返回发生错误的存储过程或触发器的名称ERROR_LINE():返回发生错误的语句在存储过程中的行号
完整示例:带错误处理的存储过程
下面创建一个插入用户数据的存储过程,其中会校验用户年龄是否合法,同时使用TRY和CATCH块捕获可能的错误,比如主键冲突、年龄不合法、数据类型错误等:
-- 创建示例用户表
CREATE TABLE TestUser (
UserId INT PRIMARY KEY,
UserName NVARCHAR(50),
UserAge INT CHECK (UserAge >= 0 AND UserAge <= 120)
);
GO
-- 创建带错误处理的存储过程
CREATE PROCEDURE Insert_User
@UserId INT,
@UserName NVARCHAR(50),
@UserAge INT
AS
BEGIN
SET NOCOUNT ON; -- 不返回受影响的行数信息,提升性能
BEGIN TRY
-- 开启事务,保证数据一致性
BEGIN TRANSACTION;
-- 校验年龄合法性,不合法主动抛出错误
IF @UserAge < 0 OR @UserAge > 120
BEGIN
THROW 50001, '用户年龄必须在0到120之间', 1;
END
-- 插入用户数据,可能出现主键冲突等错误
INSERT INTO TestUser (UserId, UserName, UserAge)
VALUES (@UserId, @UserName, @UserAge);
-- 提交事务
COMMIT TRANSACTION;
-- 返回成功提示
SELECT '用户插入成功' AS Result;
END TRY
BEGIN CATCH
-- 回滚事务,保证数据一致性
IF @@TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION;
END
-- 记录错误信息到临时表,实际场景可记录到错误日志表
DECLARE @ErrorLog TABLE (
ErrorTime DATETIME,
ErrorNumber INT,
ErrorMessage NVARCHAR(4000),
ProcedureName NVARCHAR(200),
ErrorLine INT
);
INSERT INTO @ErrorLog
VALUES (
GETDATE(),
ERROR_NUMBER(),
ERROR_MESSAGE(),
ERROR_PROCEDURE(),
ERROR_LINE()
);
-- 返回错误信息给调用端
SELECT
'操作失败' AS Result,
ERROR_NUMBER() AS ErrorCode,
ERROR_MESSAGE() AS ErrorDesc,
ERROR_PROCEDURE() AS ProcName,
ERROR_LINE() AS ErrorLineNum;
END CATCH
END
GO
存储过程的调用测试
我们可以通过不同的参数调用上面的存储过程,验证错误处理的效果:
正常插入场景
-- 正常插入数据 EXEC Insert_User @UserId = 1, @UserName = '张三', @UserAge = 25;
执行后会返回用户插入成功的结果。
年龄不合法场景
-- 插入年龄不合法的数据 EXEC Insert_User @UserId = 2, @UserName = '李四', @UserAge = 150;
此时会触发主动抛出的错误,CATCH块捕获后返回错误提示,且不会插入错误数据。
主键冲突场景
-- 插入重复主键的数据 EXEC Insert_User @UserId = 1, @UserName = '王五', @UserAge = 30;
主键冲突属于运行时错误,会被TRY块捕获,CATCH块执行回滚并返回冲突相关的错误信息。
注意事项
- TRY和CATCH块必须成对出现,且不能嵌套在其他语句的中间,只能作为批处理的一部分。
- 不是所有的错误都会被CATCH块捕获,比如严重的服务器级别错误、编译时错误(比如语法错误)不会触发CATCH块。
- 如果存储过程中使用了事务,一定要在CATCH块中判断事务状态并做回滚,避免出现悬挂事务。
- THROW语句用于主动抛出自定义错误,需要指定错误号、错误描述和状态,错误号建议大于50000,避免和系统错误号冲突。