SQL server命令行执行SQL脚本并忽略错误的行 sql语句执行错误什么意思

您所在的位置:网站首页 跳过错误继续执行sql SQL server命令行执行SQL脚本并忽略错误的行 sql语句执行错误什么意思

SQL server命令行执行SQL脚本并忽略错误的行 sql语句执行错误什么意思

2024-07-12 08:20| 来源: 网络整理| 查看: 265

SQL server命令行执行SQL脚本并忽略错误的行 sql语句执行错误什么意思_嵌套

通常,我们并不直接使用数据库,而是根据用户输入,准备好SQL语句,然后将SQL语句发送给SQL Server执行。在这个过程中,总会发生一些我们“预料不到”,或者“即使预料到也无法处理”的问题。比如:

SQL Server突然宕机或重启磁盘/内存不够用,无法插入数据因为各种约束导致SQL语句无法执行……

这些情况通常被称之为:

异常

异常有时候也被称之为“错误(error)”,但同学们注意区分“SQL语法(syntax)错误”和“异常”:

SQL语法错误:SQL语句本来就是错的,无论如何都无法执行。异常:SQL语句本身没有错误,正常情况下能够顺利执行。产生的原因是“预料不到”,或者“即使预料到也无法处理”的外部问题。

我们有时候写的SQL语句,解析(Parse)成功,但执行就会报错,这就是一种“广义的”异常。

SQL server命令行执行SQL脚本并忽略错误的行 sql语句执行错误什么意思_嵌套_02

T-SQL中“狭义的”异常是:严重级别大于 10 但不终止数据库连接的错误。这种错误,可以被TRY...CATCH捕获。捕获之后干嘛呢?

看一下下面这个例子:

一起帮中的帮帮币可以进行交易。在数据库中我们使用表BangMoney记录每个用户帮帮币的持有情况,表结构和数据如下所示(极度精简版):

CREATE TABLE BangMoney( [Name] NVARCHAR(25), Balance INT ) INSERT BangMoney VALUES(N'陈元', 100) INSERT BangMoney VALUES(N'幸龙泰', 100)

那么我们当发生一场交易的时候,比如 幸龙泰 给 陈元

UPDATE BangMoney SET Balance -= 50 WHERE [Name] = N'幸龙泰'; UPDATE BangMoney SET Balance += 50 WHERE [Name] = N'陈元';

如果说一切OK,幸龙泰的 帮帮币 变成50,陈元的 帮帮币 变成 150。

但是,假如幸龙泰再一次给陈元100个帮帮的时候,SQL Server刚执行完第一条UPDATE语句,正要执行第二次UPDATE语句的时候就遇到了异常!会发生什么情况呢?

为了演示方便,我们添加一个Balance不能小于0的约束:

ALTER TABLE BangMoney ADD CONSTRAINT CK_BangMoney_Balance CHECK(Balance>=0)

在一批次中执行如下SQL语句:

UPDATE BangMoney SET Balance -= 100 WHERE [Name] = N'幸龙泰'; UPDATE BangMoney SET Balance += 100 WHERE [Name] = N'陈元';

查看运行结构,我们会发现:

第一行幸龙泰的UPDATE因为50-100=-50违反了约束CK_BangMoney_Balance,报错而终止执行;但第二行陈百万的UPDATE仍然得以顺利执行

SQL server命令行执行SQL脚本并忽略错误的行 sql语句执行错误什么意思_嵌套_03

结果就是陈百万“平白无故”多得了100个帮帮币!

SQL server命令行执行SQL脚本并忽略错误的行 sql语句执行错误什么意思_SQL_04

这显然是不能够接受的。怎么办呢?这就需要使用到我们之前已经学过的:

事务

配合SQL Server的异常处理机制,以确保:

正常情况下,两句UPDATE顺利执行(提交:COMMIT)当发生异常的时候,两句UPDATE都不执行(回滚:ROLLBACK)

具体的SQL代码如下:

BEGIN TRY BEGIN TRANSACTION UPDATE BangMoney SET Balance -= 100 WHERE [Name] = N'幸龙泰'; UPDATE BangMoney SET Balance += 100 WHERE [Name] = N'陈元'; COMMIT -- 没有异常就会提交事务 END TRY BEGIN CATCH ROLLBACK -- 出现异常就会回滚事务 END CATCH

代码可以被分成两个部分,并相互配合:

TRY...CATCH:BEGIN TRY 和 END TRY (以下简称“TRY块”)指明了异常处理作用的范围,凡是在其中的SQL代码,如果执行中: 遇到了的异常,就会立即停止执行其后面的内容,直接运行 BEGIN CATCH 和 END CATCH(以下简称“CATCH块”)中的语句;没有任何异常,就会跳过CATCH块,继续运行其他SQL语句事务处理:BEGIN TRANSACTION 显式的 声明了事务的起始位置,如果: 没有任何异常,由COMMIT TRANSACTION将事务提交否则,由ROLLBACK将事务回滚

运行上述代码,然后检查BangMoney表,你会发现幸龙泰和陈元的Balance都没有发生改变——这是事务的回滚机制起了作用。

然后将交易的数量由100改成1,以避免因与约束冲突而造成的异常产生,语句就能够顺利执行,同时改变幸龙泰和陈元的Balance ——这是事务的提交机制起了作用。

注意:你可能会发现,在CATCH块中,即使不写ROLL BACK,似乎也能达到一样的效果?但是,这其实是不一样的。

演示:

将UPDATE语句顺序颠倒使用PRINT @@TRANCOUNT查看当前活跃事务,并额外运行一个COMMIT

需要明白:

之前“一样的效果”纯粹是因为第二个UPDATE语句没能被执行(再一次理解“异常机制”)没有运行COMMIT会导致事务没有结束

另外一种写法:

BEGIN TRAN BEGIN TRY UPDATE BangMoney SET Balance-=200 WHERE [Name] = N'幸龙泰'; UPDATE BangMoney SET Balance+=200 WHERE [Name] = N'陈元'; COMMIT SELECT * FROM BangMoney END TRY BEGIN CATCH ROLLBACK END CATCH

对比:

BEGIN TRY UPDATE BangMoney SET Balance += 5;

BEGIN TRAN UPDATE BangMoney SET Balance += 5;

另外,注意运行上述SQL代码不会再“报错”了。这通常被称之为“错误被‘吞’了”,这样我们就再也得不到具体的错误信息,这并不是一件好事。一种处理办法是我们自己记录这个错误等等,但更通常的办法是利用

THROW

再次将异常抛出,代码如下:

BEGIN CATCH ROLLBACK; -- 注意因为后面THROW的存在,这里的;必须加上(语法原因) THROW; END CATCH

这样我们就能在回滚的同时还看到报错信息了,而且还可以利用SQL Server提供的工具记录错误日志等……

事务嵌套和SAVE

有时候我们会在事务中再“嵌套”一个事务,比如:

-- TRY... BEGIN TRANSACTION UPDATE BangMoney SET Balance += 100 WHERE [Name] = N'陈元'; BEGIN TRY -- 再嵌套一个异常捕获 BEGIN TRAN -- 再嵌套一个事务 UPDATE BangMoney SET Balance -= 100 WHERE [Name] = N'幸龙泰'; COMMIT TRAN END TRY BEGIN CATCH ROLLBACK TRAN -- 一直回顾到最外/顶层的事务 END CATCH COMMIT TRANSACTION -- CATCHT...

这样写,你希望的可能是:如果嵌套中的事务出现异常,就回滚嵌套事务,不影响其他部分。但实际上这是不行的!嵌套事务中的回滚,会一直回顾到最外/顶层的事务。

演示:

这有些或许有些难以理解,你可以直接记忆:

镶嵌事务提交时从内到外依次提交回滚内部事务的同时会回滚到外部事务的起点只有最外面的COMMIT才是真正有效的提交

或者按这种方式理解:

SQL SERVER 并不支持真正意义上的事务嵌套BEGIN TRANSACTION,并不会总是真的打开一个新的事务,而是设置一个记录点 并将@@TRANCOUNT+1,(所以@@TRANCOUNT可以显示当前活跃的事务数量)每一次COMMIT,会将@@TRANCOUNT-1,直到当@@TRANCOUNT=0时,所有事务被真正的提交ROLLBACK将@@TRANCOUNT直接归零IF @@TRANCOUNT>0 -- 如果当前没有活跃事务,ROLLBACK会报异常 ROLLBACK

这种行为会给开发人员带来很多困惑,尤其调用别人书写的、甚至加密过的SQL代码块(存储过程或函数)的时候。

解决的办法是在事务中设置一个

savepoint

然后在回滚的时候指定回滚到该savepoint:

BEGIN TRY BEGIN TRAN SAVE TRAN inner_tran --设立一个事务点(savepoint) UPDATE BangMoney SET Balance -= 100 WHERE [Name] = N'幸龙泰'; COMMIT TRAN END TRY BEGIN CATCH ROLLBACK TRAN inner_tran; --回滚到事务点,而不是最外层事务 COMMIT TRAN --【注意】还需要一次事务提交 END CATCH

注意savepoint并没有改变“没有真正嵌套事务”的本质:

SAVE TRAN inner_tran 只是在事务中记录了一个“保存点”,并非是真的开启了一个事务ROLLBACK TRAN inner_tran 也没有真正的回滚事务(回滚事务会结束事务),只是将事务中的执行回溯到savepoint,所以还需要COMMIT TRAN 再提交事务(哈哈,有点难以理解……),否则事务就一直处于活动状态,不会结束。

演示:

savepoint实现“嵌套事务”的效果savepoint还需要COMMIT只有最外层的事务才会真正的提交事务

有时候我们会看到没有使用BEGIN TRANSACTION,直接使用COMMIT或ROLLBACK的代码。这是因为使用了

隐式事务

它需要被显式的打开,可使用如下代码:

SET IMPLICIT_TRANSACTIONS ON -- SQL SERVER 默认是OFF的

这样事务的起始点就是SQL语句的第一行,或者上一个COMMIT或ROLLBACK结束后的第一行,作用范围直到第一个COMMIT或ROLLBACK。

我们一般不推荐隐式事务。如果偶尔确实需要使用隐式事务,记得在完成所需操作后予以关闭,以免造成混乱:

SET IMPLICIT_TRANSACTIONS OFF

最后,我们要知道:

事务四大特性(ACID)

原子性(Atomicity):事务作为一个整体不可再分,事务中的所有内容要完成都全部完成,要不完成都不完成。不能只完成一部分。一致性(Consistency):事务的运行不能破坏数据的一致性,比如各种约束等。隔离性(Isolation):正在执行的事务不能被其他事务干扰。持久性(Durablity):一旦完成(COMMIT和ROLLBACK),改变就是永久性的。比如COMMIT之后就不能再ROLLBACK。

实际上,各种关系数据库对上述1、2、4条要求都执行得较好,但对于3隔离性因为并发的原因,不得不做出妥协。

每日单词:

SQL server命令行执行SQL脚本并忽略错误的行 sql语句执行错误什么意思_SQL_05

作业:

用户(Reigister)发布一篇悬赏币若干的求助(Problem),他的帮帮币(BMoney)也会相应减少,但他的帮帮币总额不能少于0分:请综合使用TRY...CATCH和事务完成上述需求。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3