本文出处: http://www.cnblogs.com/wy123/p/6743515.html
T-SQL 编程与应用程序一样,都有异常处理机制,比如异常的捕获与异常的抛出,本文简单介绍异常捕获与异常抛出在 T-SQL 编程中的实际使用 。
异常处理简单说明
异常捕获在应用程序编程中非常常见,提供了处理程序运行时出现的任何意外或异常情况的方法刚毕业的时候对于异常处理迷茫不解,尤其是 catch 中又 throw,既然 catch 或者不 catch,都会 throw,为什么要 catch 后再 throw?catch 中到底要做什么处理?后来接触的多了开始慢慢理解了异常处理这个机制,在应用程序和 T-SQL 中应该是类似的可以简单地这样理解:对于 UI 层面, 异常捕获,个人理解就是对于可能发生异常的代码段进行捕获处理,给予用户友好的提示信息,防止应用程序崩溃(或者抛给给用户一个后台代码错误的页面)的一种做法。如果是底层方法(这个底层可以这么理解 A 方法调用 B 方法,B 方法又调用 C 方法,C 方法就是底层方法),异常处理可以是在捕获之后继续抛出给上层调用者,让调用者知道它调用的方法发生了什么问题。对于发生了异常的代码本身,要记录下来异常的原因,以便于问题的排查。比如 C 方法中发生了异常,要告诉调用他的 B 方法 "我发生了异常,异常原因是 ***", 这种的话 C 就要抛出异常,同时 C 要记录异常的信息 (通过不同方式将上面的异常原因记录下来),供后继排查问题作参考。
以上是理论基础,下面以 T-SQL 中的异常处理为例,简单介绍一下异常处理方式和要做的事情,T-SQL 中的异常处理。
catch 块中处理异常信息
首先借助下面两张表来做示例说明。一张产品信息表,有产品 Id 和价格信息
下面将通过一个新增产品信息的存储过程说明如何进行异常处理如下是新增产品信息的存储过程,存超过程根据参数,插入到 Product 表中,
在插入数据的过程中,进行了异常捕获,在 catch 代码中,有两个操作,第一步是将异常信息插入 ExceptionLog,当然,这个异常信息的格式可以自己定义,第二步抛出异常(throw),就基于上面的理论首先为什么要记录异常,这个很容易理解,A 写的存储过程给 B 去调用,B 调用的时候发生了异常,将异常信息记录下来有利于 A 去排查异常的具体原因然后抛出异常,目的是告诉 B,执行存储过程的时候产生了异常,你的操作并没有成功执行。比如下面代码,执行的时候发生了主键冲突异常,throw 的作用就是告诉调用者,执行存储过程的时候发生了异常,并将详细的错误信息抛出
当然实际中常见的异常也比较多,比如死锁,主外键冲突,执行超时,没有操作权限等等各种无法估计到的异常,包括记录异常信息的格式,可以自由选择。
此时观察 Catch 中记录的 ExceptionLog 信息,也记录了下面记录下来的异常信息目的是事后排查问题,与上面直接 "抛出" 的异常信息作用不同的是,一个是在异常的发生的时候告诉调用者,抛出异常是标明当前执行的代码发生了异常,ExceptionLog 记录异常信息是做排查分析异常原因使用
如果不抛出异常,比如如下的代码,注释掉 throw 语句,等于是在 catch 块中单纯地记录下来异常之后 "吃掉" 异常,会出现什么情况
比如在 Product 表中已经存在 Id = 1 的记录的情况下,执行如下代码是失败,但是客户端并没有任何提示,调用方并不知道发生了什么,调用存储过程的时候到底是失败了还是成功了?没有一个明确的答案。这就是 catch 中吃掉异常的后果。因此正常情况下,catch 中记录完异常之后,要 "抛出" 异常,当异常发生的时候,明确地告诉调用方发生了什么问题。
使用 throw 显式抛出异常
某些情况下需要主动抛出异常的方式来中断逻辑的执行,什么意思?就是说当前的逻辑,只有在满足一定的条件下才能执行,如果条件不满足,就要明确告诉调用方,你的条件不满足,当前逻辑无法正常执行。举个例子,还是刚才插入产品信息的存储过程,如下当插入产品信息的时候,只有产品价格大于 0 才是有效的产品信息,否则无法插入,此时就可以通过抛出异常的方式明确地告诉调用者(当然也有其他方式),你的参数不合法,使用 throw 抛出自定义异常,强制中断代码的执行。
上面的方式只是举个示例说明,正常情况下,调用方传递过来的参数都是经过校验的,不会发生太低级的错误。当然,实际应用中应该比这个复杂的多,无法保证调用者都是从用户图形界面(UI)发起的,也就是说无法通过直接的预先处理来确保输入信息的合法性对于类似上面的存储过程首先无法保证调用方永远传递过来的参数是合法有效的,其次连调用者也无法确保自己的生成的参数的逻辑永远不会发生错误。此时对于逻辑上要求非常严谨的程序来说,就需要做类似上面主动抛出异常了来中断代码的执行了。当然上面问题的处理方式也不止这一种,只是说异常处理的应用场景和方式。
throw 语句的使用
最后说一下 throw 语句,
throw 是必 raiserror 更加方便和直观的异常抛出方式,也是推荐的异常处理方式,具体差异网上一大把就不多说了 throw 有两种使用方式,抛出自定义异常和直接在 catch 块中抛出异常。抛出自定义异常的时候有三个必须参数,下面会细说,catch 块中可以直接用 throw 不需要任何参数的方式抛出捕获到的异常 throw 语句的前一句需要一分号结尾,前一句又不能保证一定有分号,所以可以直接把分号写在 throw 的前面,比如文中的; throw 50000,'Price can not be less than 0',1 写法当抛出自定义错误的时候,throw 语句有三个参数,参考如下 throw 语句安全级别默认为 16 并且不会被修改,换句话说就是 throw 语句执行之后将抛出错误,打断当前 Session 的批处理语句,throw 后面的语句将不会执行第一个参数是错误号,用户自定义错误号要大于 50000(50000 to 2147483647) 第二个参数是自定义错误信息内容,可以根据需要自定义第三个参数是 Error State,他的作用是可以标记异常发生的位置,比如同样是 "参数不能小于 0" 的错误提示,整个存储过程中有可能有两个地方进行同样的校验 就可以在两个地方使用 Error State 不同分别来抛出异常; throw 50000,'Price can not be less than 0',1 throw 50000,'Price can not be less than 0',2, 因为 Error State 不同,就可以根据具体的 Error State 更加方便地知道是哪个地方发生了异常,参考
throw 语句存储自定义异常到 messages 系统表可以将自定义的异常信息加入到 sys.messages 系统表中,先加英文的,再加中文的。然后使用的时候可以使用这个预定义的 message,而不是每次写一个字符串
- EXEC sp_addmessage
- @msgnum = 50001,
- @severity = 16,
- @msgtext =N'%s can not be less than 0',
- @lang = 'us_english';
- EXEC sp_addmessage
- @msgnum = 50001,
- @severity = 16,
- @msgtext =N'%s 不能小于0',
- @lang = '简体中文';
如下,存储过程中先通过 FORMATMESSAGE 来格式化异常信息,然后使用 throw 50001, @msg, 1; 的方式抛出异常
然后在调用存储过程是的时候,如下是异常被触发时候的抛出自定义异常的场景。
总结:本文简单介绍了 T-SQL 编程中的异常处理方式,异常处理的时候可以通过捕获异常信息并记录下来,确保代码的维护性,也为问题排查提供参考依据。 也可以在确保满足业务逻辑的条件下,主动抛出自定义异常的方式终止代码的执行,来确保业务逻辑的正确性。
来源: http://www.cnblogs.com/wy123/p/6743515.html