什么时候会用到嵌套事务 ?

为了代码复用,我们会写许多的储蓄过程,而中间如果需要使用到 transaction 难免就会发生嵌套了.

sql server 并不直接支持嵌套事务. 但它可以用一些招式来实现嵌套效果. 虽然这些招式并不优雅,也容易让了陷入迷雾.

这篇收集了一些资料来说说 sql server 中的嵌套事务.

这篇写了基本的 sql server 对 transaction 的处理方式 https://www.cnblogs.com/kymo/archive/2008/05/14/1194161.html

begin trans @@count +1

commit trans @@count -1,如果 -1 后为 0 那么永久写入磁盘, 不然继续等待下一个 commit.

rollback trans @@count = 0 (rollback + save point name 则不会 update count, count 保持)

所以一个常遇到的 bug 就是, 就是里面的 trans rollabck 后, 外面的 trans 就废掉了, 也不能 rollback 也不能 commit, 因为 count 直接被设置为 0 了。

这就是为什么说 sql server 不直接支持嵌套事务了。

那有什么方法可以实现呢 ?

这篇给出了很好的示范

https://docs.microsoft.com/en-us/sql/t-sql/language-elements/save-transaction-transact-sql?view=sql-server-ver15

https://stackoverflow.com/questions/9713350/save-transaction-vs-begin-transaction-sql-server-how-to-nest-transactions-nice

在写储蓄过程时,我们需要判断当前的 trans count 来决定如果处理 trans

开始的时候,如果当前没有 count = 0 表示没有 trans

那么储蓄过程需要负责创建 trans

然后也负责 rollback or commit.

如果当前已经有了 trans

那么储蓄过程就不要创建新的 trans 而是做一个 save point

如果没有任何 error 也不需要 commit ,把职责交给外面的人就行了.

那么遇到 error 时就要处理了,首先 rollback to save point. (不会减少 count)

然后 throw 一个 error 到外面.

大致上就是这样,核心概念就是始终只开启一个事务, 然后利用 if else begin trans + save point + rollback save point + throw error 来达到效果

储蓄过程大概长这样

GO
IF EXISTS (SELECT name FROM sys.objects
WHERE name = N'SaveTranExample')
DROP PROCEDURE SaveTranExample;
GO
CREATE PROCEDURE SaveTranExample
AS DECLARE @TranCounter INT;
SET @TranCounter = @@TRANCOUNT;
IF @TranCounter > 0
SAVE TRANSACTION ProcedureSave;
ELSE
BEGIN TRANSACTION;
print(@@TRANCOUNT)
update Products set name = 'mk100' where Id = 1;
-- Modify database.
BEGIN TRY
IF @TranCounter = 0
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @TranCounter = 0
ROLLBACK TRANSACTION;
ELSE
IF XACT_STATE() <> -1
ROLLBACK TRANSACTION ProcedureSave;
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT; SELECT @ErrorMessage = ERROR_MESSAGE();
SELECT @ErrorSeverity = ERROR_SEVERITY();
SELECT @ErrorState = ERROR_STATE(); RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
GO

调用的时候是这样的

select * from Products order by Id;
update Products set name = 'asdds' where Id = 1;
update Products set name = 'asdwf' where Id = 2; begin tran
update Products set name = 'mk100' where Id = 2;
begin try
exec SaveTranExample;
commit;
end try
begin catch
print('error')
rollback;
end catch

解释一下 XACT_STATE

在储蓄过程中,error 时其实只需要 throw 出去就好了,因为外面理应负责 rollback, 但是这里把职责交给了外面. 如果当前决定不可以 commit ,那么它会是 -1 那么储蓄过程可以什么也不做,因为外面肯定会 rollback

但如果它是可以被 commit 的情况下 1 or 0 那么只 rollback 到 save point 而已, 其余交给外面负责. 意思是只是 rollback 了这个储蓄过程, 外面如果还是想 commit 也行. 一种灵活的方式而已啦.

此外还有一个点就是隔离级别. 我们搞这些不就是为了复用 store procedure 吗.

https://docs.microsoft.com/zh-cn/previous-versions/sql/sql-server-2005/ms173763(v=sql.90)?redirectedfrom=MSDN

2 个点

1. 切换级别后,之前锁定的东西依然会保持旧的级别

2. 储蓄过程, trigger 内修改级别的话,只有在内部有效,离开储蓄过程就会自动切换回去之前的.

另外提一下, ef core 没有支持 nested transactions, 或者说应该用 transaction scope 来到达效果.

https://github.com/aspnet/EntityFrameworkCore/issues/3470

https://github.com/aspnet/EntityFrameworkCore/issues/6233

其它 refer :

https://docs.microsoft.com/en-us/sql/t-sql/functions/xact-state-transact-sql?view=sql-server-ver15

https://docs.microsoft.com/en-us/sql/t-sql/functions/xact-state-transact-sql?view=sql-server-ver15

https://www.cnblogs.com/xugang/archive/2011/04/09/2010216.html

https://stackoverflow.com/questions/16043513/sql-server-try-catch-with-xact-state

https://www.sqlskills.com/blogs/paul/a-sql-server-dba-myth-a-day-2630-nested-transactions-are-real/

https://stackoverflow.com/questions/527855/nested-transactions-in-sql-server

https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms189336(v=sql.105)?redirectedfrom=MSDN

https://stackoverflow.com/questions/9713350/save-transaction-vs-begin-transaction-sql-server-how-to-nest-transactions-nice

http://rusanu.com/2009/06/11/exception-handling-and-nested-transactions/

https://github.com/aspnet/EntityFrameworkCore/issues/3470

https://github.com/aspnet/EntityFrameworkCore/issues/6233

https://www.cnblogs.com/kymo/archive/2008/05/14/1194161.html

https://blog.csdn.net/kufeiyun/article/details/27533853

https://www.mssqltips.com/sqlservertip/4897/handling-transactions-in-nested-sql-server-stored-procedures/

https://stackoverflow.com/questions/31277829/multiple-begin-transactions

sql server 学习笔记 (nested transaction 嵌套事务)的更多相关文章

  1. 【SQL Server学习笔记】Delete 语句、Output 子句、Merge语句

    原文:[SQL Server学习笔记]Delete 语句.Output 子句.Merge语句 DELETE语句 --建表 select * into distribution from sys.obj ...

  2. 【SQL Server学习笔记】事务、锁定、阻塞、死锁 sys.sysprocesses

    http://blog.csdn.net/sqlserverdiscovery/article/details/7712068 Column name Data type Description   ...

  3. SQL server 学习笔记1

    1.查询安装的排序规则选项喝当前的排序规则服务器属性 select * from fn_helpcollations(); 2.查看当前服务器的排序规则 select serverproperty(' ...

  4. sql server 学习笔记 ( backup 备份方案 )

    做个记入就好 USE [master] SELECT bs.database_name AS 'Database Name', bs.backup_start_date AS 'Backup Star ...

  5. SQL SERVER学习笔记:临时表与表变量

    本文主要摘自徐海蔚的<Microsoft SQL SERVER企业级平台管理实践> 表变量可以作为存储过程的返回参数,而临时表不行.(存疑?表值参数只在SQL SERVER2008才开始支 ...

  6. sql server 学习笔记 ( row_number, rank, dense_rank over partition by order by )

    refer : https://blog.csdn.net/winer2008/article/details/4283539 https://www.cnblogs.com/linJie193090 ...

  7. Sql Server学习笔记

    1.指定路径创建数据库 create database student on--创建库的时候必须写 ( name=student, filename='E:\database\student.mdf' ...

  8. sql server 学习笔记

    1. 修改student表中sdept字段改为varchar类型,长度为30,并且不为空 ) not null 2. 删除student表中的address列 alter table student ...

  9. 【SQL SERVER学习笔记】Sqlserver游标的尝试

    DECLARE @ProName NVARCHAR(50)DECLARE @CityName NVARCHAR(50)DECLARE @ProId INT DECLARE @CityId INT DE ...

随机推荐

  1. P1964 【mc生存】卖东西

    P1964 [mc生存]卖东西 题解 很简单, 暴力 注意一个小点就好 代码 #include<iostream> #include<cstdio> #include<c ...

  2. 《你不知道的JavaScript(上)》笔记——this全面解析

    首先要理解调用位置: 调用位置就是函数在代码中被调用的位置(而不是声明的位置). 最重要的是要分析调用栈(就是为了到达当前执行位置所调用的所有函数). 我们关心的调用位置就在当前正在执行的函数的前一个 ...

  3. python之json处理

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于JavaScript(Standard ECMA-262 3rd Edition - Decembe ...

  4. 无法连接App Store

    试了很多网上的方法,都没有效果,最后把hosts文件清空了,就可以了,不知道是为啥,同一份hosts文件在屋里能用,公司就不能用.

  5. MySQL 5.7 源码中的目录结构

    MySQl Server的源码可以直接去Github浏览. 这里我们选择5.7版本的:https://github.com/mysql/mysql-server/tree/5.7 也可以通过: git ...

  6. Docker 跨主机网络 overlay(十六)

    目录 一.跨主机网络概述 二.准备 overlay 环境 1.环境描述 2.创建 consul 3.修改 docker 配置文件 4.准备就绪 三.创建 overlay 网络 1.在 host1 中创 ...

  7. CEIWEI USBMonitor监控驱动 OCX/SDK USB 监控精灵 USB过滤驱动

    CEIWEI USBMonitor监控精灵软件SDK USBMonitorX.dll SDK,能够嵌入到你的App程序中,从而在你的App中实现USB端口协议分析.调试USB设备的协议信息,并可以拦截 ...

  8. FineReport点击输入框直接展示下拉框列表

    引用自帆软官方文档-http://help.finereport.com/doc-view-1899.html 1. 描述 下拉框以及下拉复选框默认状态,必须点击最右侧的三角才可以实现下拉,不够人性化 ...

  9. Egret入门学习日记 --- 第二十篇(书中 9.1~9.3 节 内容 组件篇)

    第二十篇(书中 9.1~9.3 节 内容 组件篇) 第八章中的内容. 以上都是基本的Js知识,我就不录入了. 直接来看 第9章. 开始 9.1节. 以上内容告诉你,Egret官方舍弃了GUI,使用了E ...

  10. gcc 与g++的区别

    原文 http://www.cnblogs.com/wb118115/p/5969775.html 什么是gcc / g++ 首先说明:gcc 和 GCC 是两个不同的东西 GCC:GNU Compi ...