仅给出问题中显示的代码,并假定三个子过程均未进行任何显式的事务处理,那么是的,将捕获三个子过程中的任何一个错误,并且ROLLBACK
该CATCH
块中的in 将回滚所有工作。
但是以下是有关事务的一些注意事项(至少在SQL Server中):
无论您叫多少次,只有一次真实交易(第一个)BEGIN TRAN
- 您可以命名一个事务(如你在这里做)这个名字将出现在日志中,但命名仅对于第一/最外层的交易(因为再次,第一个是在交易)。
- 每次调用时
BEGIN TRAN
,无论它是否命名,事务计数器都会增加1。
- 您可以通过执行以下操作查看当前级别
SELECT @@TRANCOUNT;
- 在2或更高
COMMIT
时发出的任何命令@@TRANCOUNT
无非是一次减少一个事务计数器。
- 在at
COMMIT
发出a之前,不会提交任何内容@@TRANCOUNT
1
- 万一上面的信息不能清楚地表明:无论交易级别如何,都没有实际的交易嵌套。
保存点允许在事务中创建可以撤消的工作子集。
- 通过
SAVE TRAN {save_point_name}
命令创建/标记保存点
- 保存点标记了可以撤消而无需回滚整个事务的工作子集的开始。
- 保存点名称不必唯一,但是多次使用相同的名称仍会创建不同的保存点。
- 保存点可以嵌套。
- 保存点无法提交。
- 可以通过撤消保存点
ROLLBACK {save_point_name}
。(有关此内容,请参见下文)
- 回滚保存点将撤消最近一次对的调用之后发生的任何工作
SAVE TRAN {save_point_name}
,包括在创建被回滚的保存点之后创建的所有保存点(因此称为“嵌套”)。
- 回滚保存点不会影响事务计数/级别
SAVE TRAN
除非发出完整ROLLBACK
的整个交易记录,否则无法撤消在初始记录之前所做的任何工作。
- 只是要清楚一点:发出大于等于2 的
COMMIT
when @@TRANCOUNT
不会影响保存点(因为再次,大于1的交易级别在该计数器之外不存在)。
您不能提交特定的命名交易。事务“名称”(如果与一起提供)将COMMIT
被忽略,并且仅出于可读性而存在。
ROLLBACK
没有名称的发行将始终回滚所有交易。
ROLLBACK
具有名称的签发人必须符合以下任一条件:
交易名称和保存点名称:
存储过程本身不是隐式事务。如果没有显式事务已启动,则每个查询都是一个隐式事务。这就是为什么除非有程序上的原因要执行围绕单个查询的显式事务ROLLBACK
,否则查询中的任何错误都是该查询的自动回滚。
调用存储过程时,它必须以与@@TRANCOUNT
调用时相同的值退出。意思是,您不能:
BEGIN TRAN
在不提交的情况下在proc中启动a ,希望在调用/父进程中进行提交。
ROLLBACK
如果显式事务在调用proc之前已启动,则无法发出a ,因为它将返回@@TRANCOUNT
0。
如果退出的存储过程的事务计数大于或小于它被启动时的事务计数,则会出现类似于以下的错误:
消息266,级别16,状态2,过程YourProcName,第0行
EXECUTE之后的事务计数指示BEGIN和COMMIT语句的数量不匹配。上一个计数= X,当前计数=Y。
与常规变量一样,表变量不受事务约束。
关于可以独立调用(因此需要事务处理)或从其他proc调用(因此不需要事务处理)的proc中的事务处理:这可以通过几种不同的方式来实现。
我一直在处理,现在它好几年,似乎工作很好的方式是只BEGIN
/ COMMIT
/ ROLLBACK
在最外层。子过程调用只是跳过事务命令。我在下面概述了我在每个proc中放置的内容(嗯,每个需要事务处理的东西)。
- 在每个过程的顶部,
DECLARE @InNestedTransaction BIT;
代替simple BEGIN TRAN
,请执行以下操作:
IF (@@TRANCOUNT = 0)
BEGIN
SET @InNestedTransaction = 0;
BEGIN TRAN; -- only start a transaction if not already in one
END;
ELSE
BEGIN
SET @InNestedTransaction = 1;
END;
代替simple COMMIT
,请执行以下操作:
IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
BEGIN
COMMIT;
END;
代替simple ROLLBACK
,请执行以下操作:
IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
无论事务是在SQL Server中启动还是在应用程序层启动,此方法都应相同。
有关该事务处理中的完整模板的TRY...CATCH
构造,请参见以下DBA.SE问题的答案:我们是否需要使用C#代码以及存储过程来处理事务。
除了“基础”以外,还有一些其他细微的事务需要注意:
默认情况下,在大多数情况下,发生错误时,事务不会自动回滚/取消。只要您有适当的错误处理并ROLLBACK
自我打扰,通常这不是问题。但是,有时情况会变得复杂,例如在中止批处理错误的情况下,或者在使用时OPENQUERY
(或通常使用链接服务器),并且在远程系统上会发生错误。虽然大多数错误都可以使用来捕获TRY...CATCH
,但是有两个无法以这种方式捕获(尽管现在还不记得正在搜索哪些错误—研究)。在这些情况下,必须使用SET XACT_ABORT ON
来正确回滚事务。
SET XACT_ABORT ON会使SQL Server 立即回滚任何事务(如果一个事务处于活动状态),并在发生任何错误时中止批处理。此设置在引入该TRY...CATCH
构造的SQL Server 2005之前存在。在TRY...CATCH
大多数情况下,它可以处理大多数情况,因此在大多数情况下会使对的需求过时XACT_ABORT ON
。但是,在使用时OPENQUERY
(可能还有我目前不记得的另一种情况),您仍然需要使用SET XACT_ABORT ON;
。
在触发器内部,XACT_ABORT
被隐式设置为ON
。这将导致触发器内的任何错误,从而取消触发触发器的整个DML语句。
您应该始终具有正确的错误处理,尤其是在使用事务时。TRY...CATCH
SQL Server 2005中引入的构造提供了一种处理几乎所有情况的方法,这是对@@ERROR
每个语句之后进行测试的可喜的改进,这对于中止批处理错误没有太大帮助。
TRY...CATCH
但是,引入了一个新的“状态”。当不使用该TRY...CATCH
构造时,如果您有活动的事务并且发生错误,那么可以采用以下几种路径:
XACT_ABORT OFF
和语句中止错误:事务仍处于活动状态,并且继续处理下一条语句(如果有)。
XACT_ABORT OFF
以及大多数中止批次的错误:事务仍处于活动状态,并且继续处理下一个批次(如果有)。
XACT_ABORT OFF
以及某些中止批次的错误:事务已回滚,并且下一个批次(如果有)继续进行处理。
XACT_ABORT ON
并发生任何错误:事务已回滚,并且处理将继续进行下一批(如果有)。
但是,使用时TRY...CATCH
,批中止错误不会中止批处理,而是将控制权转移到CATCH
块。如果XACT_ABORT
是OFF
,则交易将在大多数时间仍处于活动状态,您将需要COMMIT
或很有可能需要ROLLBACK
。但是,当遇到某些中止批次的错误(例如使用OPENQUERY
)时,或者当XACT_ABORT
is时ON
,事务将处于新状态“不可提交”。在这种状态下,您将不能COMMIT
执行DML操作。您所能做的就是ROLLBACK
和SELECT
声明。但是,在这种“无法使用”状态下,事务在发生错误时已回滚,并且发出ROLLBACK
仅仅是一种形式,但这是必须完成的一种形式。
函数XACT_STATE可以用于确定事务是处于活动状态,不可提交状态还是不存在。建议(至少有人)在程序段中检查此功能CATCH
以确定结果是否为-1
(即不可提交),而不是测试if @@TRANCOUNT > 0
。但是,使用XACT_ABORT ON
,这应该是唯一可能的状态,因此似乎测试@@TRANCOUNT > 0
和XACT_STATE() <> 0
是等效的。另一方面,当XACT_ABORT
是OFF
且有一个活动的交易时,则有可能处于1
或-1
处于CATCH
块状的状态,这使得有可能发行COMMIT
而不是ROLLBACK
(尽管我无法想到某人想要COMMIT
如果交易是可提交的)。我对以下DBA.SE问题的回答中提供了有关XACT_STATE()
在CATCH
块内使用的更多信息和研究XACT_ABORT ON
:在什么情况下,当XACT_ABORT设置为ON时,可以从CATCH块内部提交事务?。请注意,在某些情况下,有一个小错误XACT_STATE()
会导致它错误地返回1
:XACT_STATE()在带有某些系统变量但没有FROM子句的SELECT中使用时返回1。
关于原始代码的注释:
- 您可以删除为交易指定的名称,因为它无济于事。
- 您不需要每次通话都使用
BEGIN
和END
EXEC
spNewBilling3
抛出错误,但是您不想回滚spNewBilling2
或spNewBilling1
,则只需[begin|rollback|commit] transaction createSavebillinginvoice
从中删除spSavesomename
。