【转】 SQL 2005 try catch
1 TRY…CATCH
1.1 用法
TRY…CATCH的语法如下:
BEGIN TRY -- TRY 模块
-- 业务处理
END TRY
BEGIN CATCH -- CATCH 模块
-- 错误处理
END CATCH
在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值。
- ERROR_NUMBER() 返回错误号
- ERROR_MESSAGE() 返回错误消息的完整文本。此文本包括为任何可替换参数(如长度、对象名或时间)提供的值
- ERROR_SEVERITY() 返回错误严重性
- ERROR_STATE() 返回错误状态号
- ERROR_LINE() 返回导致错误的例程中的行号
- ERROR_PROCEDURE() 返回出现错误的存储过程或触发器的名称
1.3 使用TRY…CATCH时的注意事项
使用TRY…CATCH时,需要注意下述事项:
- CATCH模块必须紧跟在TRY模块之后
- TRY…CATCH构造可以嵌套。这意味着可以将TRY…CATCH构造放置在其他TRY模拟和CATCH模块内。当嵌套的TRY块中出现错误时,程序控制将传递到与嵌套的TRY块关联的CATCH块
- 严重性为10或更低的错误被视为警告或信息性消息,这种错误不会导致处理跳到CATCH模块(通过RAISERROR抛出的自定义错误同样适用于此规则)。参考下面的T-SQL代码段进行测试。
BEGIN TRY
-- 业务处理
RAISERROR('start', 10, 1) -- 此句不会导致处理转到CATCH 模块
RAISERROR('warning', 11, 1) -- 此句会导致处理转到CATCH 模块
RAISERROR('finish', 10, 1) -- 由于上一句导致处理转到CATCH 模块, 故此句不会被执行
END TRY
BEGIN CATCH
-- 错误处理
SELECT
ERROR_MESSAGE() -- 返回warning
END CATCH
2 TRY…CATCH and Transaction
2.1 XACT_STATE()函数
一般在TRY…CATCH中使用事务时,会在TRY模块提交(COMMIT)事务;而在CATCH块回滚(ROLLBACK)事务。
如果要准确的控制事务的提交和回滚,则可以通过XACT_STATE()函数判断当前事务的状态,以进一步确定如何进行事务处理。
XACT_STATE()函数在SQL Server 2005及其之后的版本中可用,该函数返回下列值:
- 0 当前请求没有活动的用户事务
- 1 当前请求有活动的用户事务。请求可以执行任何操作,包括写入数据和提交事务
- -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语句将事务回滚到保存点。
3 Template
下面是一个涉及TRY…CATCH和事务处理的模板。可以参考这个模板来编写业务存储过程,如果业务处理不涉及事务,则可以去掉事务处理的那些部分。
CREATE PROC procedure_name
AS
SET NOCOUNT ON -- 当前的事务数
DECLARE
@__trancount int
SELECT
@__trancount = @@TRANCOUNT -- TRY...CATCH 处理
BEGIN TRY
-- ========================================
-- 标准的事务处理模块块
-- a. 开启事务, 或者设置事务保存点
IF @__trancount = 0
BEGIN TRAN
ELSE
SAVE TRAN __TRAN_SavePoint -- b. 这里放置处于事务中的各种处理语句 -- c. 提交事务
-- 有可提交的事务, 并且事务是在当前模块中开启的情况下, 才提交事务
IF XACT_STATE() = 1 AND @__trancount = 0
COMMIT
-- ======================================== -- ========================================
-- 为了防止TRY 中有遗漏的事务处理, 可以在TRY 模拟的结束部分做最终的判断
IF @__trancount = 0
BEGIN
IF XACT_STATE() = -1
ROLLBACK TRAN
ELSE
BEGIN
WHILE @@TRANCOUNT > 0
COMMIT TRAN
END
END
END TRY
BEGIN CATCH
-- ========================================
-- 在CATCH 模块, 应该首先处理事务
IF XACT_STATE() <> 0
BEGIN
IF @__trancount = 0
ROLLBACK TRAN
-- XACT_STATE 为-1 时, 不能回滚到事务保存点, 这种情况留给外层调用者做统一的事务回滚
-- 通过@@TRANCOUNT > @__trancount 的判断, 即使在TRY 模块中没有设置事务保存点的情况下跳到此步骤, 也不会出错
ELSE IF XACT_STATE() = 1 AND @@TRANCOUNT > @__trancount
ROLLBACK TRAN __TRAN_SavePoint
END -- ========================================
-- 错误消息处理
-- a. 获取错误信息
-- 这提提取了错误相关的全部信息, 可以根据实际需要调整
DECLARE
@__error_number int,
@__error_message nvarchar(2048),
@__error_severity int,
@__error_state int,
@__error_line int,
@__error_procedure nvarchar(126),
@__user_name nvarchar(128),
@__host_name nvarchar(128) SELECT
@__error_number = ERROR_NUMBER(),
@__error_message = ERROR_MESSAGE(),
@__error_severity = ERROR_SEVERITY(),
@__error_state = ERROR_STATE(),
@__error_line = ERROR_LINE(),
@__error_procedure = ERROR_PROCEDURE(),
@__user_name = SUSER_SNAME(),
@__host_name = HOST_NAME() -- b. 对于重要的业务处理存储过程, 应该考虑把错误记录到表中备查(这个表需要先建立)
-- 记录错误应该在没有事务的情况下进行了, 否则可能因为外层事务的影响导致保存失败
IF XACT_STATE() = 0
INSERT dbo.tb_ErrorLog(
error_number,
error_message,
error_severity,
error_state,
error_line,
error_procedure,
user_name,
host_name,
indate)
VALUES(
@__error_number,
@__error_message,
@__error_severity,
@__error_state,
@__error_line,
@__error_procedure,
@__user_name,
@__host_name,
GETDATE()) -- c. 如果没有打算在CATCH 模块中对错误进行处理, 则应该抛出错误给调用者
-- 注: 不允许在被SSB 调用的存储过程中, 将错误或者其他信息抛出
-- 因为SSB 是自动工作的, 如果它调用的存储过程有抛出信息, 则这个信息会被直接记录到SQL Server 系统日志
-- 而目前SSB 的消息数量是很多的, 这会导致SQL Server 日志爆涨掉
-- 对于被SSB 调用的存储过程, 应该在CATCH 模块中加入自己的错误处理(最简单的就是将错误记录到表中)
RAISERROR(
N'User: %s, Host: %s, Procedure: %s, Error %d, Level %d, State %d, Line %d, Message: %s ',
@__error_severity,
1,
@__user_name,
@__host_name,
@__error_procedure,
@__error_number,
@__error_severity,
@__error_state,
@__error_line,
@__error_message)
END CATCH
GO
4 Policy or suggest
在使用TRY…CATCH和事务处理时,我们有如下的一些规范需要遵守:
- 禁止在发布到production的存储过程中抛出警告或信息性消息,这包含使用PRINT语句和使用RAISERROR语句触发严重性为10或更低的错误
- 使用TRY…CATCH时,CATCH块中必须进行错误处理
- 对于重要的业务存储过程,必须建立对应的错误日志记录表,将CATCH块捕获的错误记录到错误日志记录表中
- 对于被DBA的SSB存储过程自动调用的存储过程,禁止抛出任何信息,所有的错误应该在被调用的存储过程中完成。虽然被调用的存储过程发生错误可以被DBA的SSB存储过程捕获,但由于涉及业务处理,DBA无法修复这些错误,故这些错误不应该给出DBA。而对于抛出的信息,这个不会被捕获,但由于是SSB自动调用的,因此会写入SQL Server的日志中,这可能会产生大量的日志,导致磁盘空间紧张,从而影响SQL Server运行
- 在嵌套调用的存储过程的时候,建议对存储过程的返回值做判断,以确定被调用的存储过程是否出现过问题。要获取存储过程的返回值,可以使用类似下面的调用方法(除非使用RETURN 语句手工设置返回值,否则无论被调用的存储过程中产生的错误是否被处理,只要有错误产生,返回的值都不会是0)
DECLARE
@__sp_re int EXEC @__sp_re = dbo.procedure_name IF @__sp_re <> 0
RAISERROR('has some error', 16, 1)
5 Reference
Reference Document
TRY...CATCH
URL: http://msdn2.microsoft.com/en-us/library/ms175976.aspx
SAVE TRANSACTION
URL: http://msdn2.microsoft.com/en-us/library/ms188378.aspx
【转】 SQL 2005 try catch的更多相关文章
- SQL 2005批量插入数据的二种方法
SQL 2005批量插入数据的二种方法 Posted on 2010-07-22 18:13 moss_tan_jun 阅读(2635) 评论(2) 编辑 收藏 在SQL Server 中插入一条数据 ...
- SQL 2005中char、nchar、varchar、ntext and nvarchar(max)的区别
原文地址 MS SQL大值数据类型varchar(max).nvarchar(max).varbinary(max) 在MS SQL2005及以上的版本中,加入大值数据类型(varchar(max). ...
- SQL 2005远程连接是出错(provider: SQL 网络接口, error: 28 - 服务器不支持请求的协议
SQL 2005远程连接是出错(provider: SQL 网络接口, error: 28 - 服务器不支持请求的协议 sql远程连接服务器网络sql serversqlserver 解决方法:在服务 ...
- SQL 2005 中查询或执行另外的数据库操作的方法
原文:SQL 2005 中查询或执行另外的数据库操作的方法 摘要: 如果,你想在一台数据库服务器上,查询另一个台数据服务器的数据该如何做呢?如果,你想在同一台数据服务器上,在不同的数据库之间查询数据, ...
- SQL 2005 安装数据库镜像教程
最近在搞在SQL 2005安装数据库镜像,中间遇到不少的错误,在此归纳总结,以方便有需要的朋友参考. 直接上脚本,主机部分: ---修改数据库为完整恢复模式USE master;ALTER DATAB ...
- SQL 2005/2008 连接SQL 2000报18456错误
在看文章前,你先看看下面这两个问题,考考你对MSSMS工具的掌握情况: 1: SQL 2005/2008 能连接 SQL 2000数据库服务器吗? 2: SQL 2000 能连接SQL 2005/20 ...
- SQL Server:查看数据库用户权限(SQL 2005)
1. 查看 SQL 2005 用户所属数据库角色 use yourdb go select DbRole = g.name, MemberName = u.name, MemberSID = u.si ...
- windows 10 安装 sql 2005 安装失败
windows 10 安装 sql 2005 安装失败 网上的方法记录: 安装中无法启动需要先用sp4的补丁文件sqlos.dll,sqlservr.exe 替换D:\Program Files (x ...
- SQL 2005示例库(转载)
sql2005数据库实例 从网上找还得麻烦,转了过来,点击就可以下载! 在学习SQL2005中离开不了SQL2005示例数据库,AdventureWorks数据库下载安装,,northwind数据库下 ...
随机推荐
- SQL根据现有表新建一张表
SQL根据现有表新建表,新建的这张表结构要跟现有表结构相同,但不要现有表里面的数据! 执行DML语句依据数据库类型而定: SQLITE -----复制表结构及数据到新表 CREATE TABLE TA ...
- Color Map的生成方法
/* Return a RGB colour value given a scalar v in the range [vmin,vmax] In this case each colour comp ...
- URAL 1227 Rally Championship(树的直径)(无向图判环)
1227. Rally Championship Time limit: 1.0 secondMemory limit: 64 MB A high-level international rally ...
- 英语语法最终珍藏版笔记-6“情态动词+have+ done”的含义
“情态动词+have+ done”的含义 1.Must have done的含义.“must have+过去分词”表示对过去的推测,意思是“一定已经,想必已经,准是已经….”,只用于肯定句中.例如: ...
- springMvc源码学习之:spirngMvc获取请求参数的方法
一. 通过@PathVariabl获取路径中的参数 @RequestMapping(value="user/{id}/{name}",method=RequestMeth ...
- JSBinding + SharpKit / 常见问题
运行时出现: Return a "System.Xml.XmlIteratorNodeList" to JS failed. Did you forget to export th ...
- Linux-内核缓存区和write行为
<Unix环境高级编程> 应用缓冲技术能很明显的提高系统效率.内核与外围设备的数据交换,内核与用户空间的数据交换都是比较费时的,使用缓冲区就是为了优化这些费时的操作.其实核心到用户空间的操 ...
- linearlist和linkedlist的区别 待整理
线性表在内存中是一块连续的存储空间:如:一个表中的内容是:[1,2,3]则它在内存中可能是如下存储的:1 2 3 优点:查找 通过这个结构可以看出,只要知道了第一个元素在内存中所在的位置. ...
- SQL注入脚本(基于时间)
#encoding=utf-8 import httplib import time import string import sys import urllib header = {'Accept' ...
- 【转】关于PHP的header("P3P: CP=CURa……")
对于IE来说(默认安全级别下),iframe.img.link等标签都是只发送session cookie(又叫 第一方cookie),拦截本地cookie发送(又叫第三方cookie).当这些标签跨 ...