在游标和内部事务中回滚事务

时间:2014-10-02 12:18:20

标签: sql sql-server stored-procedures transactions rollback

我是T-SQL中的新手,我遇到了一些带有事务,游标和存储过程的大型脚本。所以,我的代码是这样的(这个代码只是我的脚本结构的一个例子,实际上我在OuterProc游标内部有多个过程,InnerProc游标内有多个操作):

create proc InnerProc
as
begin
  declare @Id int

  begin tran

  declare mycursor cursor local static read_only forward_only
  for select Id
      from MyOtherTable

  open mycursor
  fetch next from mycursor into @Id

  while @@fetch_status = 0
  begin
    select 1/0

    if @@ERROR <> 0 
    begin
      rollback tran
      return @@ERROR
    end          

    fetch next from mycursor into @Id
  end

  close mycursor   
  deallocate mycursor

  commit tran
end


create proc OuterProc
as
begin

  declare @Id int

  begin tran

  declare mycursor cursor local static read_only forward_only
  for select Id
      from MyTable

  open mycursor
  fetch next from mycursor into @Id

  while @@fetch_status = 0
  begin
    exec @error = InnerProc

    if @@ERROR <> 0 
    begin
      rollback tran
      return
    end
    else
      commit tran

    fetch next from mycursor into @Id
  end

  close mycursor   
  deallocate mycursor
end

使用这种结构我有这个错误:

Msg 515, Level 16, State 2, Procedure InnerProc, Line 448
  Cannot insert the value NULL into column 'InitialQuantity', table 'MySecondTable'; column does not allow nulls. INSERT fails.
  The statement has been terminated.
Msg 266, Level 16, State 2, Procedure InnerProc, Line 0
  Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
Msg 3903, Level 16, State 1, Procedure CreateSASEExtraction, Line 79
  The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.

我的代码出了什么问题?如果innerProc内部出现问题,我希望该外部游标的所有操作都回滚并停止内部游标。如果在outerProc中出现问题,我希望该游标的所有操作都回滚,但我希望该游标继续循环...
还有更好的方法吗?

更新

在我纠正了@Bernd Linde检测到的一些错误之后,我在InnerProc中添加了一个try-catch,并命名了InnerProc事务。现在我有了这段代码:

create proc InnerProc
as
begin
  declare @Id int

  begin tran

  begin try

    declare mycursor cursor local static read_only forward_only
    for select Id
        from MyOtherTable

    open mycursor
    fetch next from mycursor into @Id

    while @@fetch_status = 0
    begin
      select 1/0

      if @@ERROR <> 0 
        return @@ERROR     

      fetch next from mycursor into @Id
    end

    close mycursor   
    deallocate mycursor

    commit tran
    return 0

  end try
  begin catch

    return @@ERROR

  end catch

end


create proc OuterProc
as
begin

  declare @Id int

  declare mycursor cursor local static read_only forward_only
  for select Id
      from MyTable

  open mycursor
  fetch next from mycursor into @Id

  while @@fetch_status = 0
  begin

    begin tran

    exec @error = InnerProc

    if @@ERROR <> 0
    begin
      rollback tran
      return
    end
    else
      commit tran

    fetch next from mycursor into @Id
  end

  close mycursor   
  deallocate mycursor
end

但现在我还有其他错误消息:

Msg 266, Level 16, State 2, Procedure InnerProc, Line 0
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 2.

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

从第一眼看,你在循环中提交事务,但是你只是在循环之外启动它们 因此,每次循环进入第二次迭代时,它将尝试提交或回滚不存在的事务,因此您将收到错误"The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION."

我建议在MSDN here

上阅读SQLServer中的事务

答案 1 :(得分:0)

经过多次尝试后,我终于明白了。

InnerProc必须只有COMMIT,而OuterProc将负责回滚。 为此,当InnerProc导致一些必须在OuterProc中捕获的错误并被强制像异常一样。 我想如何继续在OuterProc中循环,该过程必须有一个try-catch,其中强制循环并且回滚完成。

为了更好的交易号码控制,我使用了@@ TRANCOUNT。

所以我用这段代码解决了这个问题:

create proc InnerProc
as
begin
  declare @Id int

  begin try

  begin tran

    declare mycursor cursor local static read_only forward_only
    for select Id
        from MyOtherTable

    open mycursor
    fetch next from mycursor into @Id

    while @@fetch_status = 0
    begin
      select 1/0

      IF @@ERROR <> 0   
      begin

        if @@TRANCOUNT > 0
          rollback tran

        close mycursor   
        deallocate mycursor
        return @@ERROR

      end

      fetch next from mycursor into @Id

    end

    close mycursor   
    deallocate mycursor

    commit tran
    return 0

    end try
    begin catch

        close mycursor   
        deallocate mycursor

        return @@ERROR
    end catch

end


create proc OuterProc
as
begin

  declare @Id int

  declare mycursor cursor local static read_only forward_only
  for select Id
      from MyTable

  open mycursor
  fetch next from mycursor into @Id

  while @@fetch_status = 0
  begin

    begin tran

    begin try

      exec @error = InnerProc

      if @@ERROR <> 0
          RAISERROR('Exception',1,1)


      if@@TRANCOUNT > 0
          commit tran

      fetch next from mycursor into @Id, @Name, @CodeDGAE, @Code, @NUIT, @Project   

    end try
    begin catch
        if @@TRANCOUNT > 0
            rollback tran
        fetch next from mycursor into @Id, @Name, @CodeDGAE, @Code, @NUIT, @Project 
    end catch
  end

  close mycursor   
  deallocate mycursor
end