在PostgreSQL中,可以利用raise抛出非常、利用begin ... exception ... when ... then ... end的办法捕捉非常,MySQL则利用declare ... handler定义非常。SQL Server的非常处理以2008为分水岭,之前的办法非常繁琐、之后的办法则非常清爽。
本日我们就以SQL Server为例,详细解释下如何在SQL脚本中捕捉缺点。
在SQLServer2000中,捕捉非常是非常繁琐的。要通过@@ERROR跟踪SQL实行的情形,如果SQL实行正常,@@ERROR值是0,如果涌现缺点,@@ERROR返回对应的缺点号,缺点处理便是根据@@ERROR的值进行处理的。

笔者之前在SQLServer2000环境写的存储过程中,每实行一句SQL,就要随着写一句缺点判断,非常繁琐。抓个当年写的片段理解下:
在实行完一条SQL语句之后,立时判断@@error的值,如果@@error<>0,则设置缺点提示内容,并利用goto语句跳转到缺点处理部分。
MSSQL2008及往后的缺点捕捉可喜的是,从2008开始,SQLServer开始支持利用try...catch办法捕捉和处理SQL非常。不要鄙视这个变革,这可是质变,从此我们就摆脱了对非常繁琐的跟踪。
我们可以将大段的SQL脚本用try语句包裹起来,在try后面随着catch语句,当sql实行涌现缺点时,会自动跳转到catch段落进行缺点处理。我们先来看看try...catch的语法格式:
BEGIN TRY SQL段落END TRY BEGIN CATCH 处理段落END CATCH;
语法格式可能与我们想象的有点不一样,搞编程的朋友都知道,比如在Delphi中,利用的try...except...end和try...finally...end捕捉和处理非常;Java和C#的办法最优雅,利用try...catch...finally捕捉和处理非常。MSSQL的try...catch虽然比不过Java和C#的清爽,但比起每实行依据都要判断的办法,不知道要好了多少倍呢。
MSSQL抛出非常之后,供应了一系列的非常函数获取非常的解释,紧张包括以下几个内置函数:
ERROR_NUMBER() 返回缺点编号。ERROR_SEVERITY() 返回严重性。ERROR_STATE() 返回缺点状态号。ERROR_PROCEDURE() 返回涌现缺点的存储过程或触发器的名称。ERROR_LINE() 返回导致缺点的脚本中的行号。ERROR_MESSAGE() 返回缺点的完全文本。结合这些内置缺点解释的函数,我们就可以在catch段落进行相应处理了。下面我们举一个捕捉非常的例子:
begin try declare @x int=1,@y int=0; set @x=@x/@y;end trybegin catch select error_number() as 缺点编号, error_severity() as 严重性, error_state() as 缺点状态号, error_procedure() as 存储过程, error_line() as 缺点行号, error_message() as 缺点信息;end catch;
这是一个明显的被零除缺点,运行效果参看下图:
脚本中第3行,涌现了被零除的缺点,在catch段的非常捕捉部分,我们返回了缺点的解释。在系统表sys.messages中,存放着所有系统缺点的解释,“8134”则对应表中的message_id字段,对应的缺点描述如下图所示:
MSSQL每一种内置缺点,针对每种支持的措辞,都有对应的缺点描述,这里我们就不展开解释了。
利用THROW主动抛出非常很多时候,我们并不是非要等着SQL语句实行缺点才返回非常,而是根据业务须要返回提示缺点。比如用户输入的用户名密码不匹配,我们抛出非常,奉告客户端用户名或密码错,这就要利用throw语句来抛出非常了。
throw的语法格式如下:
THROW 缺点编号, 缺点信息, 缺点状态缺点编号:表示非常的常量或变量,类型int,哀求大于即是50000且小于即是2147483647,以避开内置缺点内码。缺点信息:描述非常的字符串或变量。message类型为nvarchar(2048)。缺点状态:0到 255之间的常量或变量,指示与关联的状态。
这里的缺点编号,进入catch块后,利用error_numer()进行捕捉,缺点信息利用error_message()捕捉,缺点状态利用error_state()捕捉,Throw的严重性代码永久都是16。
参看下面的示例脚本:
begin try declare @username nvarchar(100)='张三'; declare @password varchar(255)='12345'; if not (@username='张三' and @password='123456') throw 50000,'用户名或密码缺点!',1;end trybegin catch select error_number() as 缺点编号, error_severity() as 严重性, error_state() as 缺点状态号, error_procedure() as 存储过程, error_line() as 缺点行号, error_message() as 缺点信息;end catch;
下图是运行效果:
这是人为抛出的非常,与SQL语句实行的非常是不同的,但都会跳转到catch段落统一处理。
一样平常在存储过程中,我们在try开始前实行begin tran,语句块末了实行commit tran,在catch模块实行rollback tran,从而实现事务的掌握。这里就不再赘述了。
如果我们能活用throw触发非常、利用try ... cath ...捕捉和处理非常,我们的脚本就显得更加健壮安全。这对一个精良的数据库程序员显得非常主要。您说呢?