1TRY…CATCH
1.1用法
TRY…CATCH的语法如下:
BEGINTRY--TRY模块
--业务处理
ENDTRY
BEGINCATCH--CATCH模块
--错误处理
ENDCATCH
在BEGIN TRY和END TRY中的是TRY模块;BEGIN CATCH和END CATCH之间是CATCH模块。TRY模块中的任何一条T-SQL出错时,将跳到CATCH模块(TRY模块中,出错的T-SQL后的语句不会被执行)。
1.2错误处理函数
使用TRY…CATCH时,必须在CATCH模块中对错误处理处理,如果不处理,则SQL Server不会给出任何提示,这样就不会知道是否有错误发生。
在CATCH模块中,可以使用下面的这些函数来实现错误处理(这些函数只能用在CATCH模块中),在其他位置使用时,这些函数返回NULL值。
nERROR_NUMBER()返回错误号
nERROR_MESSAGE()返回错误消息的完整文本。此文本包括为任何可替换参数(如长度、对象名或时间)提供的值
nERROR_SEVERITY()返回错误严重性
nERROR_STATE()返回错误状态号
nERROR_LINE()返回导致错误的例程中的行号
nERROR_PROCEDURE()返回出现错误的存储过程或触发器的名称
1.3使用TRY…CATCH时的注意事项
使用TRY…CATCH时,需要注意下述事项:
nCATCH模块必须紧跟在TRY模块之后
nTRY…CATCH构造可以嵌套。这意味着可以将TRY…CATCH构造放置在其他TRY模拟和CATCH模块内。当嵌套的TRY块中出现错误时,程序控制将传递到与嵌套的TRY块关联的CATCH块
n严重性为10或更低的错误被视为警告或信息性消息,这种错误不会导致处理跳到CATCH模块(通过RAISERROR抛出的自定义错误同样适用于此规则)。参考下面的T-SQL代码段进行测试。
BEGINTRY
--业务处理
RAISERROR('start',10,1)--此句不会导致处理转到CATCH模块
RAISERROR('warning',11,1)--此句会导致处理转到CATCH模块
RAISERROR('finish',10,1)--由于上一句导致处理转到CATCH模块,故此句不会被执行
ENDTRY
BEGINCATCH
--错误处理
SELECT
ERROR_MESSAGE()--返回warning
ENDCATCH
2TRY…CATCH andTransaction
2.1XACT_STATE()函数
一般在TRY…CATCH中使用事务时,会在TRY模块提交(COMMIT)事务;而在CATCH块回滚(ROLLBACK)事务。
如果要准确的控制事务的提交和回滚,则可以通过XACT_STATE()函数判断当前事务的状态,以进一步确定如何进行事务处理。
XACT_STATE()函数在SQL Server 2005及其之后的版本中可用,该函数返回下列值:
n0当前请求没有活动的用户事务
n1当前请求有活动的用户事务。请求可以执行任何操作,包括写入数据和提交事务
n-1当前请求具有活动的用户事务,但法提交事务或回滚到保存点;它只能请求完全回滚事务
2.2嵌套事务与事务保存点
SQL Server中的事务处理可以嵌套。可以通过全局变量@@TRANCOUNT查询当前连接的活动事务数(也就是事务嵌套的层数)。如果该变量值为0,则表示当前连接没有启用任何事务;如果该变量值>1,则表示当前连接存在事务嵌套。
SQL Server自动维护全局变量@@TRANCOUNT的值。当使用BEGIN TRANSACTION语句时,SQL Server将@@TRANCOUNT加1;使用COMMIT TRANSACTION或COMMIT WORK时,SQL Server将@@TRANCOUNT递减1;而使用ROLLBACK TRANSACTION时(不包括使用ROLLBACK TRANSACTION savepoint_name的情况),所有的事务都被回滚,对应的,@@TRANCOUNT直接递减为0。
嵌套事务一般出现在嵌套存储过程时,但一般不建议使用嵌套事务,这会增加事务控制的难度。一般建议的事务处理方法,是在可能出现嵌套事务的地方,通过判断@@TRANCOUNT的值来确定当前的事务数,如果当前没有事务,则开启新的事务;如果已经有事务,则使用SAVE TRANSACTION savepoint_name语句设置事务保存点,以便在需要回滚当前处理的时候,可以通过ROLLBACK TRANSACTION savepoint_name语句将事务回滚到保存点。
3Template
下面是一个涉及TRY…CATCH和事务处理的模板。可以参考这个模板来编写业务存储过程,如果业务处理不涉及事务,则可以去掉事务处理的那些部分。
CREATEPROCprocedure_name
AS
SETNOCOUNTON
--当前的事务数
DECLARE
@__trancountint
SELECT
@__trancount=@@TRANCOUNT
-- TRY...CATCH处理
BEGINTRY
-- ========================================
--标准的事务处理模块块
-- a.开启事务,或者设置事务保存点
IF@__trancount=0
BEGINTRAN
ELSE
SAVETRAN__TRAN_SavePoint
-- b.这里放置处于事务中的各种处理语句
-- c.提交事务
--有可提交的事务,并且事务是在当前模块中开启的情况下,才提交事务
IFXACT_STATE()=1AND@__trancount=0
COMMIT
-- ========================================
-- ========================================
--为了防止TRY中有遗漏的事务处理,可以在TRY模拟的结束部分做最终的判断
IF@__trancount=0
BEGIN
IFXACT_STATE()=-1
ROLLBACKTRAN
ELSE
BEGIN
WHILE@@TRANCOUNT>0
COMMITTRAN
END
END
ENDTRY
BEGINCATCH
-- ========================================
--在CATCH模块,应该首先处理事务
IFXACT_STATE()<>0