在SQL Server2005中进行错误捕捉。

 
任何程序都可能出现错误,在 SQL Server 中执行 Transact-SQL 也不例外。如果在 Transact-SQL 中发生了错误,一般有两种捕捉错误的方法,一种是在客户端代码(如 C# Delphi ) 中使用类似 try...catch 的语句进行捕捉;另外一种就是在 Transact-SQL 中利用 Transact-SQL 本身提供的错误捕捉机制进行捕捉。如果是因为 Transact-SQL 语句的执行而产生的错误,如键值冲突,使用第一种和第二种方法都可以捕捉,但是如果是逻辑错误,使用客户端代码进行捕捉就不太方便。因此,本文就如何使用 Transact-SQL 进行错误捕捉进行了讨论。
一、非致命错误 (non-fatal error) 的捕捉
通过执行 Transact-SQL 而产生的错误可分为两种:致命错误 (fatal error) 和非致命错误 (non-fatal error) 。在 Transact-SQL 中只可以捕捉非致命错误 ( 如键值冲突 ) ,而无法捕捉致命错误 ( 如语法错误 ) 。在 Transact-SQL 中可以通过系统变量 @@ERROR 判断最近执行的一条语句是否成功执行。如果发生了错误, @@Error 的值大于 0 ,否则值为 0 。下面举一个例子说明 @@ERROR 的使用。
假设有一个表 table1 ,在这个表中有两个字段 f1 f2 。其中 f1 是主键。
INSERT INTO table1 VALUES(1, 'aa') INSERT INTO table1 VALUES(1, 'bb')  -- 这条语句将产生一个错误 IF @@ERROR > 0 PRINT ' 键值冲突 '
当执行第二条语句时发生键值冲突错误, @@ERROR 被赋为错误号 2627 ,因此输出结果显示 ' 键值冲突 ' 。使用 @@ERROR 系统变量时需要注意, @@ERROR 只记录最近一次执行的 Transact-SQL 语句所发生的错误,如果最近一次执行的 Transact-SQL 没有发生错误, @@ERROR 的值为 0 。因此,只能在被捕捉的那条 Transact-SQL 语句后使用 @@ERROR
SQL Server 中,不仅可以捕捉系统提供的错误,还可以自定义错误。有两种方法可以定义错误信息。
1 、使用 sp_addmessage 系统存储过程添加错误信息,然后使用 RAISERROR 抛出错误。
sp_addmessage 将错误号,错误级别、错误描述等信息添加到系统表中,然后使用 RAISERROR 根据相应的错误号抛出错误信息。用户自定义的信息应该从 50001 开始。
EXEC sp_addmessage @msgnum = 50001, @severity = 16, @msgtext = 'sql encounter an error(%s).', @lang = 'us_english'
EXEC sp_addmessage @msgnum = 50001, @severity = 16, @msgtext = 'sql 遇到了一个错误 (%1!).'
如果使用的 SQL Server 版本是非英语版本,在添加本地错误信息时必须首先添加英文的错误信息。错误描述可以象 c 语言中的 printf 的格式字符串一样使用参数,如 %s %d 。但要注意的是在英文版的错误信息中要使用 %s %d 等形式,而在本地化的错误信息中要使用 %1! %2! 等形式,在每个 %?(1 <= ? <= n) 后需要加一个 ! ,而且 %? 的数目必须和英文版的错误信息的参数一致。
在未插入本地化错误信息时, RAISERROR 将使用英文版的错误信息。当插入本地化错误信息时, RAISERROR 使用本地化的错误信息。
RAISERROR(50001, 16, 1, ' 测试 ') 输出的结果: 服务器 : 消息 50001 ,级别 16 ,状态 1 ,行 1 sql 遇到了一个错误 ( 测试 ).
其中 ' 测试 ' 字符串通过 %1 传入本地化的错误描述字符串中。 2 、直接使用 RAISERROR 将错误抛出。
使用第一种方法虽然使 Transact-SQL 语句看上去更整洁(这种方法类似于在编程语言中使用常量定义错误信息,然后在不同的地方通过错误编号引用这些错误信息。 ) ,但是这样做却使错误信息和数据库的耦合度增加,因为如果将这些带有 RAISERROR Transact-SQL 放到别的 SQL Server 数据库上执行,由于在其它的数据库中还未添加错误信息,因此会产生 RAISERROR 调用错误,除非使用 sp_addmessage 将所需的错误信息再加入到其它的数据库中。
基于上述原因, RAISERROR 不仅可以根据错误代码抛出错误信息,也可以直接通过错误描述格式字符串抛出错误信息。
RAISERROR('sql 遇到了一个错误 (%s)', 16, 1, ' 测试 ')
二、逻辑错误的捕捉
在实际应用中,更多的是由于某些业务要求而产生的逻辑错误。这些错误无法通过 @@ERROR 进行捕捉。如果使用客户端代码进行捕捉,那么 Transact-SQL 必须一条一条地执行。如果使用存储过程,那么发生在存储过程内部的逻辑错误就很难在客户端代码中进行捕捉,因此,下面将讨论如何使用 Transact-SQL 捕捉逻辑错误。
所谓逻辑错误,就是在执行完 Transact-SQL 后,执行结果与业务要求的结果不符而产生的。为了说明如何处理逻辑错误,我们再建立一个表 table2 ,这个表的结构和 table1 完全一样,只是 f1 字段不再是主键了。然后建立一个存储过程,它的功能是在 table1 table2 中同时插入一条记录,但是这条记录必须满足两个条件。
1 f1 值不能大于 100
2 、要插入的记录在 table1 中不存在,如果存在,在 table1 table2 中都不插入这条记录。
CREATE PROCEDURE p1(@Num int) AS DECLARE @Error int, @RowCount int BEGIN TRANSACTION INSERT INTO table2 VALUES(@Num, 'p') IF @Num > 100 BEGIN RAISERROR('%s 的值不能大于100。', 16, 1, '@Num')   ROLLBACK TRANSACTION RETURN 1 END ELSE BEGIN SELECT f1 FROM table1 WHERE f1 = @Num IF @@ROWCOUNT > 0 BEGIN RAISERROR('table1中已经存在%d了。', 16, 1, @Num) ROLLBACK TRANSACTION RETURN 2 END ELSE BEGIN INSERT INTO table1 VALUES(@Num, 'p') COMMIT TRANSACTION RETURN 0 END END
在这个存储过程中一开始使用 BEGIN TRANSACTION 显示地开始一个事务,然后当上述两种错误发生时使用 ROLLBACK TRANSACTION 恢复到初始状态,如果成功插入,使用 COMMIT TRANSACTION 提交改变。可以通过如下语句进行调用。
DECLARE @ErrNum int EXEC @ErrNUm = p1 2 PRINT @ErrNum
可以通过 @ErrNum 得到 p1 返回的错误代码,如果返回 0 ,表示执行成功。
sql Server2005 中错误捕捉的新功能
虽然在以前的 SQL Server 版本中可以通过一些技巧实现错误捕捉,但有时需要增加一些额外的开销,如在 p1 中使用了 SELECT 语句。庆幸的是在 SQL Server2005 中提供了和大多数编程语言类似的 try...catch 错误捕捉功能,从而使 Transact-SQL 第一次可以真正地进行错误捕捉。使用 try...catch 可以将 p1 的下半部分改写为如下形式。
ELSE BEGIN BEGIN TRY INSERT INTO table1 VALUES(@Num, 'p')   COMMIT TRANSACTION RETURN 0 END TRY BEGIN CATCH RAISERROR('table1 中已经存在%d了。', 16, 1, @Num) ROLLBACK TRANSACTION RETURN 2 END CATCH END
可以看出,这个改写的部分未使用 SELECT 查询 table1 中是否已经有了某条记录,而是通过数据库的约束来进行判断的。如果键值冲突,就产生了错误,这样 SQL 语句就直接跳到 BEGIN CATCH 中执行错误处理代码。这样做效率要比上一个版本高得多,而且如果将 RAISERROR 去掉, p1 就不会抛出任何错误,只是返回了一个错误码,这样有利于客户端代码进行处理。

Transact-SQL中进行错误捕捉,如果使用的是SQL Server2005,我的建议是尽量使用try...catch,因此它会捕捉到未预料到的错误,并且会使Transact-SQL更容易维护。当然,这样做就无法将Transact-SQL移植到SQL Server2000或更低的版本上运行,要是想写通用的Transact-SQL,还是使用传统的方法捕捉错误吧! <script type=text/javascript> google_ad_client = "pub-6430022987645146"; google_ad_slot = "5930513112"; google_ad_width = 728; google_ad_height = 15; </script>

 v
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值