SQL - EXECUTE之后的事务计数表示不匹配

时间:2014-08-01 14:44:07

标签: sql sql-server stored-procedures

我正在使用MS SQL Server Management Studio 2012中的存储过程。

我正在重用其他过程中的一些代码,这包括使用EXEC命令调用另一个存储过程。

此过程将把对象的信息作为参数,重置该对象的状态,然后查找相关(下游)对象并重置其状态。

代码正在运行,但即使它没有处于错误状态,也需要对重置和相关对象的状态进行一些小改动。为了解决这个问题,我在主循环之前创建了一个迭代相关对象的变量,然后当遇到具有错误状态的第一个对象时,它设置变量的值。变量本身是BIT

以下是程序:

USE [DB_TEST]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER          PROC [dbo].[resetTasksInErrorStatus]
    @TaskId                         INT,
    @SubjectId                      INT,
    @ProjectProtocolId              INT,
    @TimePointId                    INT,
    @WorkbookId                     INT,
    @LogCreatorUserId               INT,
    @LogReasonTypeId                INT,
    @LogOtherReason                 VARCHAR(256),
    @SignatureCaptured              BIT,
    @ManualChangeTaskStatus         BIT,
    @ErrorIfModifiedAfter           DATETIME,       
    @NewTaskStatusID                INT,            
    @ProcessorUserId                INT

AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION ResetTasksInError

    -- Make sure the task has not been modified since the reset was requested
    IF(EXISTS(
        SELECT ti.* FROM TaskInstance as ti
        WHERE ti.ModifyDate > @ErrorIfModifiedAfter
            AND ti.TaskId = @TaskId
            AND ti.SubjectId = @SubjectId
            AND ti.ProjectProtocolId = @ProjectProtocolId
            AND ti.TimePointId = @TimePointId
            AND ti.WorkbookId = @WorkbookId))
    BEGIN
        RAISERROR('The task to be reset was modified before the reset could complete', 16, 1);
        GOTO error
    END

    -- Get all downstream task instances
    SELECT *
    INTO #downstreamTaskInstances
    from dbo.fnGetTaskInstancesDownstream
    (
        @TaskId,
        @SubjectId,
        @TimePointId
    )

    -- Get the previous task status
    DECLARE @OldTaskStatus INT;
    SELECT TOP 1 @OldTaskStatus = ti.TaskStatusTypeId FROM TaskInstance as ti
    WHERE ti.TaskId = @TaskId
        AND ti.SubjectId = @SubjectId
        AND ti.TimePointId = @TimePointId

    -- Reset the task
    EXEC setTaskStatus
        @TaskID,
        @SubjectId,
        @ProjectProtocolId,
        @TimePointId,
        @WorkBookId,
        @OldTaskStatus,
        @NewTaskStatusID,
        @ProcessorUserId,
        @LogCreatorUserId,
        @LogReasonTypeId,
        @LogOtherReason,
        @SignatureCaptured,
        @ManualChangeTaskStatus,
        NULL,
        NULL,
        NULL,
        1

    -- Check if setTaskStatus rolled back our transaction
    IF(@@TRANCOUNT = 0)
    BEGIN
        RAISERROR('Error in sub procedure. Changes rolled back.', 16, 1);
        RETURN
    END

    -- Set a boolean variable to determine whether downstream tasks should be reset
    DECLARE @ResetDownstreamTasks BIT = 0;
    --Set @ResetDownstreamTasks = 0;

    -- Create a cursor of the downstream tasks
    DECLARE downstreamCursor CURSOR FOR
        SELECT TaskId, TimePointId, ProcessorUserId, TaskStatus, WorkBookId, ProjectProtocolId, IsManual, TaskStatus
        FROM #downstreamTaskInstances
    OPEN downstreamCursor

    -- Reset each downstream task to unprocessed
    DECLARE @CursorTaskID INT;
    DECLARE @CursorTimePointID INT;
    DECLARE @CursorProcessorUserId INT;
    DECLARE @CursorTaskStatusID INT;
    DECLARE @CursorWorkBookId INT;
    DECLARE @CursorProjectProtocolId INT;
    DECLARE @CursorIsManual BIT;
    Declare @CursorTaskStatus INT;
    FETCH NEXT FROM downstreamCursor INTO @CursorTaskID, @CursorTimePointId, @CursorProcessorUserId, @CursorTaskStatusID, @CursorWorkBookId, @CursorProjectProtocolId, @CursorIsManual, @CursorTaskStatus
    WHILE @@FETCH_STATUS = 0 AND @@ERROR = 0 AND @@TRANCOUNT = 1
    BEGIN
        -- Check if the task is in error status, and then make sure it is not an manual task.
        -- Manual tasks should never be in error status, so there is no need to reset them.
        if @CursorTaskStatus = 10 and @CursorIsManual <> 1
        begin
                SET @ResetDownstreamTasks = 1;

                EXEC setTaskStatus
                    @CursorTaskID,
                    @SubjectId,
                    @CursorProjectProtocolId,
                    @CursorTimePointId,
                    @CursorWorkBookId,
                    @CursorTaskStatusID,
                    1, -- Unprocessed
                    @CursorProcessorUserId,
                    @LogCreatorUserId,
                    @LogReasonTypeId,
                    @LogOtherReason,
                    @SignatureCaptured,
                    @ManualChangeTaskStatus,
                    NULL,
                    NULL,
                    NULL,
                    0
        end;


        if @ResetDownstreamTasks = 1
        begin
            EXEC setTaskStatus
                @CursorTaskID,
                @SubjectId,
                @CursorProjectProtocolId,
                @CursorTimePointId,
                @CursorWorkBookId,
                @CursorTaskStatusID,
                6, -- Inspected
                @CursorProcessorUserId,
                @LogCreatorUserId,
                @LogReasonTypeId,
                @LogOtherReason,
                @SignatureCaptured,
                @ManualChangeTaskStatus,
                NULL,
                NULL,
                NULL,
                0
        end

      FETCH NEXT FROM downstreamCursor INTO @CursorTaskID, @CursorTimePointId, @CursorProcessorUserId, @CursorTaskStatusID, @CursorWorkBookId, @CursorProjectProtocolId, @CursorIsManual, @CursorTaskStatus
    END

    DROP TABLE #downstreamTaskInstances
    CLOSE downstreamCursor
    DEALLOCATE downstreamCursor

    -- Check if setTaskStatus rolled back our transaction
    IF(@@TRANCOUNT = 0)
    BEGIN
        RAISERROR('Error in sub procedure. Changes rolled back.', 16, 1);
        RETURN
    END

    IF(@@ERROR <> 0)
    BEGIN
        GOTO ERROR
    END

    COMMIT TRANSACTION  ResetTasksInError
    RETURN

    ERROR:
        RAISERROR('Error encountered. Changes rolled back.', 16,1);
        ROLLBACK TRANSACTION ResetTasksInError
        RETURN
END

当我运行该程序时,我得到了这些错误:

Msg 266, Level 16, State 2, Procedure setTaskStatus, Line 0
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
Msg 16943, Level 16, State 4, Procedure resetTasksInErrorStatus, Line 197
Could not complete cursor operation because the table schema changed after the cursor was declared.
Msg 3701, Level 11, State 5, Procedure resetTasksInErrorStatus, Line 200
Cannot drop the table '#downstreamTaskInstances', because it does not exist or you do not have permission.
Msg 50000, Level 16, State 1, Procedure resetTasksInErrorStatus, Line 207
Error in sub procedure. Changes rolled back.

如果我注释掉SET ...语句,则该过程运行并且有效(不符合要求)。

我四处寻找类似的问题,但没有一个能解决我的问题。

关于SET语句,我有什么遗漏吗?

它是否以某种方式影响@@TRANCOUNT变量?

我确实看到一些帖子提到问题很可能是在这个调用的存储过程中,但是我有点犹豫,因为存储过程有效,并且这些错误只在尝试设置值时出现变量。

2 个答案:

答案 0 :(得分:1)

只是一个猜测..但我认为会发生以下情况:

  1. 在事务中创建临时表。
  2. 启动游标并迭代它直到其中一个内部sps出错。
  3. 事务被回滚,因此临时表将与事务
  4. 一起使用
  5. 光标给出架构更改错误
  6. drop error
  7. 所以你应该检查一个内部sps中的错误。

    希望它有所帮助。

答案 1 :(得分:0)

我找到了一种使存储过程正常工作的方法。

我所做的是从if语句中删除了存储过程EXEC语句,该语句设置了变量的值。

所以循环中的代码如下所示:

WHILE @@FETCH_STATUS = 0 AND @@ERROR = 0 AND @@TRANCOUNT = 1
    BEGIN
        -- Check if the task is in error status, and then make sure it is not an manual task.
        -- Manual tasks should never be in error status, so there is no need to reset them.
        if @CursorTaskStatus = 10 and @CursorIsManual <> 1
        begin
                SET @ResetDownstreamTasks = 1;
        end;


        if @ResetDownstreamTasks = 1
        begin
            EXEC setTaskStatus
                @CursorTaskID,
                @SubjectId,
                @CursorProjectProtocolId,
                @CursorTimePointId,
                @CursorWorkBookId,
                @CursorTaskStatusID,
                6, -- Inspected
                @CursorProcessorUserId,
                @LogCreatorUserId,
                @LogReasonTypeId,
                @LogOtherReason,
                @SignatureCaptured,
                @ManualChangeTaskStatus,
                NULL,
                NULL,
                NULL,
                0
        end

      FETCH NEXT FROM downstreamCursor INTO @CursorTaskID, @CursorTimePointId, @CursorProcessorUserId, @CursorTaskStatusID, @CursorWorkBookId, @CursorProjectProtocolId, @CursorIsManual, @CursorTaskStatus
    END