sql server中嵌套事务*
转自 https://www.cnblogs.com/guanjie20/archive/2013/02/17/2914488.html
我们在写事务时经常遇到的问题如下:
消息 266,级别 16,状态 2,过程 sp1,第 0 行
EXECUTE
后的事务计数指示
BEGIN
和
COMMIT
语句的数目不匹配。上一计数 = 1,当前计数 = 0。
消息 3903,级别 16,状态 1,过程 sp2,第 15 行
ROLLBACK
TRANSACTION
请求没有对应的
BEGIN
TRANSACTION
。
如果这只是一个单独的事务引起的,那么很好解决,我们只要检查下是否遗漏了匹配的BEGIN tran 和 COMMIT tran即可,但是如果2个存储过程都是用事务写的,那么就即使每个存储过程的事务写法都正常,也会报这个错误,
这是因为只要子事务里有回滚语句:如ROLLBACK 那么全局的@@TRANCOUNT被直接置为0了,导致父事务提交时发现 @@TRANCOUNT=0 报错 ,sql server会认为当前不存在任何事务,在父存储过程中任何的COMMIT TRAN或
ROLLBACK 语句都会找不到它对应的 BEGIN TRAN
下面我们用一个实例来看下:
假设有一张表,ID为非自增主键
USE [TestDB]
GO /****** Object: Table [dbo].[test] Script Date: 02/17/2013 15:44:35 ******/
SET ANSI_NULLS ON
GO SET QUOTED_IDENTIFIER ON
GO SET ANSI_PADDING ON
GO CREATE TABLE [dbo].[test](
[ID] [bigint] NOT NULL,
[UserID] [bigint] NULL,
[Name] [varchar](50) NULL,
CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] GO SET ANSI_PADDING OFF
GO
我们常规的写一个插入的子存储过程如下:
USE [TestDB]
GO
/****** Object: StoredProcedure [dbo].[innertranv1] Script Date: 02/17/2013 15:46:46 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO --内层事务存储过程,演示如何处理才能在嵌套的事务存储过程中正确处理事务
ALTER PROCEDURE [dbo].[innertranv1]
@ID BIGINT ,
@UserID BIGINT ,
@Name VARCHAR(50)
AS
BEGIN
SET XACT_ABORT ON
BEGIN TRAN IF(EXISTS(SELECT TOP 1 * FROM dbo.test WHERE ID=@ID))
BEGIN
ROLLBACK
RETURN 0 ;
END --业务逻辑开始 INSERT dbo.test
( ID, UserID, Name)
VALUES ( @ID,
@UserID,
@Name
)
--业务逻辑结束 IF @@error <> 0
BEGIN
ROLLBACK
RETURN 0;
END COMMIT
SET XACT_ABORT OFF;
RETURN 1 ; END
调用的父存储过程如下:
USE [TestDB]
GO
/****** Object: StoredProcedure [dbo].[outertranv2] Script Date: 02/17/2013 16:09:09 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO -- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <外层存储过程>
-- =============================================
ALTER PROCEDURE [dbo].[outertranv2]
@ID BIGINT ,
@UserID BIGINT ,
@Name VARCHAR(50)
AS BEGIN TRAN
DECLARE @result INT
EXEC @result = innertranv1 @ID =@ID, @UserID =@UserID, @Name = @Name
IF ( @result <= 0 )
BEGIN
ROLLBACK TRAN ;
RETURN ;
END
COMMIT TRAN
我们执行父存储过程:
USE [TestDB]
GO DECLARE @return_value int EXEC @return_value = [dbo].[outertranv2]
@ID = 0,
@UserID = 0,
@Name = N'' SELECT 'Return Value' = @return_value GO
第一次提交正常,再次执行就会出现如下错误:
消息 266,级别 16,状态 2,过程 innertranv1,第 0 行
EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配。上一计数 = 1,当前计数 = 0。
消息 3903,级别 16,状态 1,过程 outertranv2,第 18 行
ROLLBACK TRANSACTION 请求没有对应的 BEGIN TRANSACTION。
如何解决?我们修改子存储过程如下:
USE [TestDB]
GO
/****** Object: StoredProcedure [dbo].[innertran] Script Date: 02/17/2013 16:26:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--内层事务存储过程,演示如何处理才能在嵌套的事务存储过程中正确处理事务
ALTER PROCEDURE [dbo].[innertran]
@ID BIGINT ,
@UserID BIGINT ,
@Name VARCHAR(50)
AS
BEGIN
DECLARE @TRANCOUNT int=(select @@TRANCOUNT) SET XACT_ABORT ON
SET @TRANCOUNT=(select @@TRANCOUNT)
PRINT '未进入子事务前全局@@TRANCOUNT'+CAST(@TRANCOUNT AS VARCHAR(50))
BEGIN TRAN tran1 --开始事务
SAVE TRAN tranpoint --保存事务点
SET @TRANCOUNT=(select @@TRANCOUNT)
PRINT '进入子事务后全局@@TRANCOUNT'+CAST(@TRANCOUNT AS VARCHAR(50)) IF(EXISTS(SELECT TOP 1 * FROM dbo.test WHERE ID=@ID))
BEGIN
ROLLBACK TRAN tranpoint ; --回滚保存点的事务
COMMIT TRAN tran1 ; --提示当前事务
SET @TRANCOUNT=(select @@TRANCOUNT)
PRINT '回滚子事务后全局@@TRANCOUNT'+CAST(@TRANCOUNT AS VARCHAR(50)) RETURN 0 ;
END --业务逻辑开始 INSERT dbo.test
( ID, UserID, Name)
VALUES ( @ID,
@UserID,
@Name
)
--业务逻辑结束 IF @@error <> 0
BEGIN
ROLLBACK TRAN tranpoint ; --回滚保存点的事务
COMMIT TRAN tran1 ; --提示当前事务
SET @TRANCOUNT=(select @@TRANCOUNT)
PRINT '回滚子事务后全局@@TRANCOUNTT'+CAST(@TRANCOUNT AS VARCHAR(50)) RETURN 0;
END COMMIT TRAN tran1 ;
SET XACT_ABORT OFF;
SET @TRANCOUNT=(select @@TRANCOUNT)
PRINT '提交子事务后全局@@TRANCOUNT'+CAST(@TRANCOUNT AS VARCHAR(50)) RETURN 1 ; END
父过程如下:
USE [TestDB]
GO
/****** Object: StoredProcedure [dbo].[outertran] Script Date: 02/17/2013 16:27:13 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <外层存储过程>
-- =============================================
ALTER PROCEDURE [dbo].[outertran]
@ID BIGINT,
@UserID BIGINT,
@Name VARCHAR(50)
AS
DECLARE @TRANCOUNT int=(select @@TRANCOUNT)
PRINT '未进入父事务前全局@@TRANCOUNT:'+CAST(@TRANCOUNT AS VARCHAR(50)) BEGIN TRAN
SET @TRANCOUNT=(select @@TRANCOUNT)
PRINT '进入父事务后全局@@TRANCOUNT:'+CAST(@TRANCOUNT AS VARCHAR(50)) DECLARE @result INT EXEC @result = innertran @ID = @ID, @UserID = @UserID, @Name =@Name IF ( @result <= 0 )
BEGIN
ROLLBACK TRAN ;
SET @TRANCOUNT=(select @@TRANCOUNT)
PRINT '回滚父事务后全局@@TRANCOUNT:'+CAST(@TRANCOUNT AS VARCHAR(50)) RETURN ;
END
COMMIT TRAN
SET @TRANCOUNT=(select @@TRANCOUNT)
PRINT '提交父事务后全局@@TRANCOUNT:'+CAST(@TRANCOUNT AS VARCHAR(50))
调用父存储过程:
USE [TestDB]
GO DECLARE @return_value int EXEC @return_value = [dbo].[outertran]
@ID = 0,
@UserID = 0,
@Name = N'' SELECT 'Return Value' = @return_value GO
结果如下:
未进入父事务前全局@@TRANCOUNT:0
进入父事务后全局@@TRANCOUNT:1
未进入子事务前全局@@TRANCOUNT:1
进入子事务后全局@@TRANCOUNT:2
回滚子事务后全局@@TRANCOUNT:1
回滚父事务后全局@@TRANCOUNT:0
不会再报"EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配"之类的错误了,实际上就是在每个嵌套的子过程中标明当前事务点,每个子事务 只提交/回滚 子事务点,而不是回滚整个事务!
实际开发中还是会出现事务错乱的情况,如在try...catch...中
sql server中嵌套事务*的更多相关文章
- SQL Server误区30日谈 第26天 SQL Server中存在真正的“事务嵌套”
误区 #26: SQL Server中存在真正的“事务嵌套”错误 嵌套事务可不会像其语法表现的那样看起来允许事务嵌套.我真不知道为什么有人会这样写代码,我唯一能够想到的就是某个哥们对SQL Serve ...
- T-SQL查询进阶--SQL Server中的事务与锁
为什么需要锁 在任何多用户的数据库中,必须有一套用于数据修改的一致的规则,当两个不同的进程试图同时修改同一份数据时,数据库管理系统(DBMS)负责解决它们之间潜在的冲突.任何关系数据库必须支持事务的A ...
- SQL Server中的高可用性(2)----文件与文件组
在谈到SQL Server的高可用性之前,我们首先要谈一谈单实例的高可用性.在单实例的高可用性中,不可忽略的就是文件和文件组的高可用性.SQL Server允许在某些文件损坏或离线的情况下,允 ...
- SQL Server中SELECT会真的阻塞SELECT吗?
在SQL Server中,我们知道一个SELECT语句执行过程中只会申请一些意向共享锁(IS) 与共享锁(S), 例如我使用SQL Profile跟踪会话86执行SELECT * FROM dbo.T ...
- Microsoft SQL Server中的事务与并发详解
本篇索引: 1.事务 2.锁定和阻塞 3.隔离级别 4.死锁 一.事务 1.1 事务的概念 事务是作为单个工作单元而执行的一系列操作,比如查询和修改数据等. 事务是数据库并发控制的基本单位,一条或者一 ...
- SQL Server中TOP子句可能导致的问题以及解决办法
简介 在SQL Server中,针对复杂查询使用TOP子句可能会出现对性能的影响,这种影响可能是好的影响,也可能是坏的影响,针对不同的情况有不同的可能性. 关系数据库中SQL语句只 ...
- 在SQL Server中为什么不建议使用Not In子查询
在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题: 结果不准确 查询性能低下 下面 ...
- SQL Server中提前找到隐式转换提升性能的办法
http://www.cnblogs.com/shanksgao/p/4254942.html 高兄这篇文章很好的谈论了由于数据隐式转换造成执行计划不准确,从而造成了死锁.那如果在事情出现之前 ...
- SQL Server中行列转换 Pivot UnPivot
SQL Server中行列转换 Pivot UnPivot PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PI ...
随机推荐
- iOS学习——Quartz2D学习之UIKit绘制
iOS学习——Quartz2D学习之UIKit绘制 1.总述 在IOS中绘图技术主要包括:UIKit.Quartz 2D.Core Animation和OpenGL ES.其中Core Animati ...
- 【Storm篇】--Storm中的同步服务DRPC
一.前述 Drpc(分布式远程过程调用)是一种同步服务实现的机制,在Storm中客户端提交数据请求之后,立刻取得计算结果并返回给客户端.同时充分利用Storm的计算能力实现高密度的并行实时计算. 二. ...
- jni c++
java与c/c++之间的数据交互 JNI 上述两篇文章对jni的讲解比较详细,各有利弊,就文章1来说,开门见山,直接阐述了java和C/C++的交互方式:文章2是一篇百度文库 ...
- Object Pooling(对象池)实现
在文章开始之前首先要思考的问题是为什么要建立对象池.这和.NET垃圾回收机制有关,正如下面引用所说,内存不是无限的,垃圾回收器最终要回收对象,释放内存.尽管.NET为垃圾回收已经进行了大量优化,例如将 ...
- Java基础8:深入理解内部类
更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...
- Chapter 4 Invitations——18
But they were all in, and Edward was speeding away. 但是他们都在里面了之后,Edward就加速走了. I drove home slowly, ca ...
- What can university bring to you?
前言 大学真的是一个神奇的地方,它能带给你的东西超乎你的想象. 当我刚进大学的时候,觉得它和初中,高中,没什么不同,就只是换了地方而已,但是当我现在从里面出来之后,才真的发现,我已经真的不是当年那个自 ...
- Spring Boot分布式系统实践【扩展1】shiro+redis实现session共享、simplesession反序列化失败的问题定位及反思改进
前言 调试之前请先关闭Favicon配置 spring: favicon: enabled: false 不然会发现有2个请求(如果用nginx+ 浏览器调试的话) 序列化工具类[ ...
- LeetCode专题-Python实现之第20题:Valid Parentheses
导航页-LeetCode专题-Python实现 相关代码已经上传到github:https://github.com/exploitht/leetcode-python 文中代码为了不动官网提供的初始 ...
- js内存深入学习(二)
继上一篇文章 js内存深入学习(一) 3. 内存泄漏 对于持续运行的服务进程(daemon),必须及时释放不再用到的内存.否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃. 对于不再用到的内 ...