存储过程结构和并发问题

时间:2017-02-07 02:39:14

标签: sql sql-server tsql

有人在SQL Server 2008中编写了以下存储过程。我认为这不是实现他所需要的最佳方法之一。

正如您所看到的,我们需要在某些条件下更新MyTable,而不是在另一条件下更新MyTable。只要有条件允许用户更新MyTable,我们就不希望多个用户同时更新Begin Transaction。问题在于编写此过程的方式所有变量都是使用select语句设置的,然后sp_getapplock开始。

但是考虑到这个过程的结构,最好是执行一个sp_getapplock过程,只允许一个用户进行更新(考虑到它的结构,这可能适用于这个过程,但是我在那里阅读{{ 1}}可能导致死锁),其他选项是执行可序列化的READ_COMMITED

但是在序列化的情况下,这个存储过程不允许其中一个并发用户(例如2)仍然选择col1,col2,col3和col4的旧值并填充@onGoingSession之类的变量,{当另一个完成更新并释放行时(通过可序列化),{1}}等等那些旧值。自事务完成之后,它就是第二个用户的转向。我确实想阻止其他用户阅读正在修改的行。

我们不应该在声明变量之后立即移动begin transaction,并且在它之后执行可序列化。因此,没有并发用户应该读取旧值并填充变量,而其中一个用户(显然是已经获得行锁的用户)正在更新锁定行但尚未提交。

@timeDiff

1 个答案:

答案 0 :(得分:0)

以下内容会将事务简化为单个update语句,并使用逻辑来更新case表达式处理的各个列。后处理可处理任何错误并产生预期的各种结果。

create procedure dbo.MyTableProc( @UserId as Char(20) )
  as
  begin

  declare @CounterOfResync as TinyInt;
  declare @Error as Int;
  declare @ErrorMessage as VarChar(200);
  declare @OngoingSession as Bit = 1;
  declare @PriorUsername as Char(20);
  declare @TimeDiff as SmallInt;
  declare @WarningMessage as VarChar(300);

  update MyTable
    set
      @CounterOfResync = Col1,
      @PriorUsername = UserId,
      @OngoingSession = Col3,
      @TimeDiff = DateDiff( minute, Col4, Current_Timestamp ),
      UserId = @UserId,
      Col2 = case when Col3 = 0 and Col1 > 0 and DateDiff( minute, Col4, Current_Timestamp ) >= 5 then 1 else Col2 end,
      Col3 = case when Col1 = 0 and Col3 = 0 then 1 else Col3 end,
      Col4 = Current_Timestamp
    set @Error = @@Error;

    if ( @CounterOfResync = 1 )
      if ( @TimeDiff >= 360 )
        begin
        if ( @Error = 0 )
          begin
          set @WarningMessage = 'An unfinished session for user ' + @PriorUsername + ' is going on for the past ' +
            Cast( @TimeDiff as VarChar(10) ) + ' minutes but updates from ' + @UserId + ' are successful';
          RaIsError( @WarningMessage, 7, 1 );
          end
        else
          return @Error;
        end
      else
        begin
        if ( @Error = 0 )
          begin
          set @ErrorMessage = 'A session of updates for '+ @PriorUsername + ' is already in progress concurrent updates are not allowed';
          RaIsError( @ErrorMessage, 8, 1 );
          end
        else
          return @Error;
        end
    else
      if ( @OngoingSession = 0 and @CounterOfResync = 0 )
        return @Error
      else
        -- ...

为尝试浏览现有代码并将其转换为陌生人的错误而道歉。我的意图是提供您可能选择遵循的(误)方向,而不是完整的代码。