SQL Server开始和回滚不起作用

时间:2018-03-14 21:20:43

标签: sql sql-server transactions

我在使用回滚和事务时遇到问题。

设置如下:我有1个主存储过程和1个内部循环存储过程。两者都有自己的开始和回滚事务。循环开始事务不能作为现有应用程序的一部分进行更改,而是我现在添加的外部主要部分,这会导致问题。

我在较小的应用程序上重现了这个问题。

我有两行数据:

enter image description here

循环逻辑:

循环执行的操作是通过每条记录并检查现金是否> = 5然后它会将表上的状态更新为2并提交它。

如果现金不是> = 5,那么我将引发一个错误,跳转到开始捕获并回滚事务。它会将状态更新为3。

问题:

如果我运行只有现金> = 5的记录意味着没有回滚,那么所有帐户都会成功更新为2.

但是,当我运行上面显示的数据时(带现金的行< 5)。第一个记录确实设置为2,但是当处理第二行时,第一个记录重置回先前的状态1,这是没有意义的。第二条记录正确地将状态更新为3并回滚任何更改,但这应该是与第一行不同的事务。我不知道为什么他们有联系。

此外,如果我从主存储过程中删除第一个begin事务,那么它将运行正常,但是我需要一个主进程的begin事务,所以如果主存储过程中的任何操作失败,我可以回滚所有内容。知道为什么记录回滚循环存储过程,即使它应该在移动到下一行之前提交它吗?

主存储过程:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE sp_runMain
    @pBulkID INT
AS
BEGIN
    DECLARE @errorMessage AS VARCHAR(255) = ''
    DECLARE @error AS INT = 0

    BEGIN TRY
            BEGIN TRANSACTION

            exec spRun_Process @pBulkID
            if(@@TRANCOUNT > 0)
            COMMIT TRANSACTION
        END TRY
        BEGIN CATCH
            set @error = @@ERROR 
            set @errorMessage= ERROR_MESSAGE()
            ROLLBACK TRANSACTION

        END CATCH

    END
    GO

内循环存储过程:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO

    ALTER PROCEDURE spRun_Process
        @pBulkID INT
    AS
    BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    Declare @totalCount As int = 0
    Declare @rowCount As int = 1

    Declare @processID as int = 0
    Declare @name as varchar(255) = ''
    Declare @cash as int = 0


    Declare @processTable as table(
    rowid int identity(1,1),
    processID int ,
    name varchar(255) ,
    cash int ,
    status int
    )

    INSERT INTO @processTable
    SELECT Process_ID, Name , Cash , Status FROM dbo.process WHERE BulkID = @pBulkID

    SELECT @totalCount = count(*) FROM dbo.Process WHERE BulkID = @pBulkID

    WHILE @rowCount <= @totalCount
    BEGIN
        BEGIN TRY
            BEGIN TRANSACTION

            SELECT @processID = pt.processID,
                    @name = pt.name ,
                    @cash = pt.cash
            FROM @processTable AS pt
            WHERE rowid = @rowCount

            if @cash >= 5
            BEGIN
                PRINT 'WORKS!'
            END
            ELSE
            BEGIN
                RAISERROR ('cash less than 5', 16, 1)
            END

            UPDATE dbo.Process set status = 2 where Process_ID = @processID and BulkID = @pBulkID

            COMMIT TRANSACTION
        END TRY
        BEGIN CATCH
            ROLLBACK TRANSACTION
            UPDATE dbo.Process set status = 3 where Process_ID = @processID and BulkID = @pBulkID
        END CATCH

        SET @rowCount = @rowCount + 1
     END 
    END
    GO

创建数据表的脚本:

    SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Process](
    [Process_ID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [varchar](255) NULL,
    [Cash] [int] NULL,
    [Status] [int] NULL,
    [BulkID] [int] NULL,
 CONSTRAINT [PK_Process] PRIMARY KEY CLUSTERED 
(
    [Process_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

1 个答案:

答案 0 :(得分:1)

我认为您需要一个表来模拟&#39;回滚&#39;你正在寻找的过程。创建一个存储&#39;建议更改的表格。具有您需要进行所需更新所需的所有列。在整个循环中插入此表。在巨型过程的最后(在第一次提交之外),通过并对数据进行更改(如果需要,在此处添加回滚/提交,尽管我将此最后一个过程写为基于集合的查询到一次完成所有更改而不是循环...使用脚本检测所有会导致需要回滚的记录,并在建议的更改中有一个列,记录它将导致回滚)。

作为额外的好处,如果您输入了一个&#39;日期&#39;并且&#39;日期处理&#39;列的建议更改&#39;表,它制作了一个不错的审计/跟踪日志(带有日期处理的条目的记录被归档,其中记录为null的记录正在等待处理)。