详细的出错信息:
这是我错误的存储过程,在这个存储过程里,我想做的是开始一个事务,设置这个事务的保存点,更新库存,如果库存更新失败,或者没有符合条件的数据行被更新,就返回出错,否则,添加提货单,如果提货单添加时出错,同样返回到一开始设置的保存点,否则,我就返回成功的信息
create proc a_stationery_useAdd ( @return int out, @rid int, @eid int, @trueName varchar(30), @num int, @useDay smalldatetime, @operatorEid int, @operatorTrueName varchar(30), @uid int, @una varchar(30), @remark varchar(400) ) as
begin transaction trn_useAdd
save transaction stk_useAdd
--update stock
update T_stationery_stock
set stocks = stocks - @num
where rid = @rid and ( stocks > @num or stocks = @num )
if @@error<>0 or @@rowcount = 0
begin
select @return = -2
raiserror('办公用品无效,或者库存量小于领用数量', 16 ,1)
rollback transaction stk_useAdd
return
end
--add bill of lading
insert into T_stationery_billLading
( rid, eid, trueName, num, useDay, operatorEid, operatorTrueName, uid, una, createTime, remark )
values
( @rid, @eid, @trueName, @num, @useDay, @operatorEid, @operatorTrueName, @uid, @una, getdate(), @remark )
if @@error<>0 or @@rowcount = 0
begin
select @return = -3
rollback transaction stk_useAdd
return
end
select @return = 1
commit transaction
分析上面的代码,可以发现两个错误的地方
1、save transaction stk_useAdd设置在事务一开始的时候,这个不是错误,就是这样的设置是重复的,没有用的,因为
begin transaction trn_useAdd
save transaction stk_useAdd
这样的连续两个语句,使stk_useAdd和trn_useAdd都是保存在同一个状态下,这两个保存点,是完全一样的,所以没有必要在一开始的时候设置保存点
2、就是sql给出的出错信息“EXECUTE 后的事务计数指出缺少了 COMMIT 或 ROLLBACK TRANSACTION 语句。原计数 = 0,当前计数 = 1。”
这个出错信息是因为我没有很好的理解事务和保存点的关系,保存点是在事务内部的
不带 savepoint_name 和 transaction_name 的 ROLLBACK TRANSACTION 回滚到事务的起点。嵌套事务时,该语句将所有内层事务回滚到最远的 BEGIN TRANSACTION 语句。在这两种情况下,ROLLBACK TRANSACTION 均将 @@TRANCOUNT 系统函数减为 0。ROLLBACK TRANSACTION savepoint_name 不减少 @@TRANCOUNT。
ROLLBACK TRANSACTION 语句若指定 savepoint_name 则不释放任何锁。
就是说,我在trn_useAdd事务里,设置了一个保存点stk_useAdd,在执行出错的时候,我把数据rollback到stk_useAdd,就是stk_useAdd是在事务内部的,事务trn_useAdd本身,并没有被提交,也没有被回滚,所以@@TRANCOUNT不会清零,由trn_useAdd造成的锁,也不会被解锁
所以,如果是在事务里rollback到一个保存点,后return,就需要在return前提交或者回滚事务
看下面这一段,我把保存点设置在事务运行的中间,这种形式的保存点,和返回才是正确的
begin transaction trn_example
update tableName set fieldName = @value
save transaction stk_savePoint
update tableName2 set fieldName2 = @value2
if @@error<>0
begin
rollback transaction stk_savePoint
commit transaction
return
end
else
begin
if @@rowcount = 0
rollback transaction
end
commit transaction
如果第二个update出现错误,就返回到第一个update后提交,并且使用return截断后面的语句继续运行
如果第二个update正确执行,就是更新的行数是0,就回滚整个事务,把第一个update执行的结果也取消