我有2个程序,即Proc1和Proc2。 我在proc2内部执行proc1。两个过程中都有多个DML操作。 proc1的输出在proc2中用于DML操作。 如果proc2中发生错误,则 如何在两个proc中处理事务以回滚所有DML操作?
我应该在两个proc中都写事务吗?
答案 0 :(得分:1)
我们使用基于http://www.sommarskog.se/error_handling/Part1.html的通用错误处理程序,在适用的情况下,我们会将其包含在(嵌套的)交易中,以确保对链进行正确的管理:
CREATE PROCEDURE [dbo].[sp_ErrorHandler](@caller VARCHAR(255))
AS BEGIN
SET NOCOUNT ON;
DECLARE @errmsg NVARCHAR(2048), @severity TINYINT, @state TINYINT, @errno INT, @lineno INT;
SELECT @errmsg=REPLACE(ERROR_MESSAGE(), 'DatabaseException: ', 'DatabaseException: '+QUOTENAME(@caller)+' --> ')
, @severity=ERROR_SEVERITY()
, @state=ERROR_STATE()
, @errno=ERROR_NUMBER()
, @lineno=ERROR_LINE();
IF @errmsg NOT LIKE 'DatabaseException%' BEGIN
SELECT @errmsg=N'DatabaseException: '+QUOTENAME(@caller)+N', Line '+LTRIM(STR(@lineno))+N', Error '+LTRIM(STR(@errno))+N': '+@errmsg;
END;
RAISERROR('%s', @severity, @state, @errmsg);
END;
(在master数据库中编译并标记为系统过程)
我们按如下方式使用此错误处理程序。在演示中,我有一个使用事务的外部proc和内部proc。
CREATE PROCEDURE dbo.uspOuterProc
AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
EXEC dbo.uspInnerProc;
PRINT 1;
COMMIT;
END TRY
BEGIN CATCH
IF @@trancount > 0
ROLLBACK TRANSACTION;
EXEC master.dbo.sp_ErrorHandler @caller = 'dbo.uspOuterProc';
END CATCH;
END;
GO
CREATE PROCEDURE dbo.uspInnerProc
AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
PRINT 2;
SELECT 1 / 0;
PRINT 3;
COMMIT;
END TRY
BEGIN CATCH
IF @@trancount > 0
ROLLBACK TRANSACTION;
EXEC master.dbo.sp_ErrorHandler @caller = 'dbo.uspInnerProc';
END CATCH;
END;
GO
编译并运行后:
EXEC dbo.uspOuterProc
您应该得到以下结果:
2
Msg 50000, Level 16, State 1, Procedure sp_ErrorHandler, Line 13 [Batch Start Line 48]
DatabaseException: [dbo.uspOuterProc] --> [dbo.uspInnerProc], Line 12, Error 8134: Divide by zero error encountered.
答案 1 :(得分:0)
您可以在外部过程中处理事务(在您的情况下为proc2)。如果proc1中会发生任何错误,则proc2事务处理程序将对其进行处理。 假设不会直接调用proc1,它将在proc2内部调用。
答案 2 :(得分:0)
有3个基本的事务处理语句(还有一些我不愿提及的高级语句):
BEGIN TRANSACTION
:将@@TRANCOUNT
会话变量提高1。如果它从0变到1,则表示事务开始。任何大于1的值都将使同一笔交易继续进行。COMMIT
:将@@TRANCOUNT
会话变量降低1。如果它从1变为0,则该交易被标记为已完成,并将影响自此以来所做的所有更改它是第一次创建的。ROLLBACK
:只要将@@TRANCOUNT
会话变量的值至少设置为1或更高,就将其减小为0(无论该值是多少)。这将关闭事务并还原自首次创建以来所做的所有更改。嵌套交易是一堆BEGIN TRANSACTION
语句的组合。完全落实事务并使更改永久生效的唯一点是,当COMMIT
会将事务计数从1减少到0时。这意味着每个{{1 }}您像金字塔一样被处死。
检查以下示例:
COMMIT
当您有一个执行另一个SP的SP并都有它们的事务时,您唯一需要关心的就是BEGIN TRANSACTION
错误并执行正确的BEGIN TRANSACTION
SELECT @@TRANCOUNT -- 1
BEGIN TRANSACTION
SELECT @@TRANCOUNT -- 2
COMMIT TRANSACTION
SELECT @@TRANCOUNT -- 1 (no change is permanent yet, not even the last one)
BEGIN TRANSACTION
SELECT @@TRANCOUNT -- 2
ROLLBACK
SELECT @@TRANCOUNT -- 0 (all changes were discarded)
IF 有一个正在进行中的公开/活动交易(如果不是CATCH
语句将失败,表明没有任何回滚内容。
一个非常基本的ROLLBACK
如下所示:
ROLLBACK
如果您想深入研究在SQL Server上处理事务的最佳方法,可以阅读this post。