我有这样一个存储过程,是用来做销售的:
CREATE PROCEDURE [update_kc_minus]
(@czyname  [varchar](50),@doctorname  [varchar](50)=null)
AS
SET XACT_ABORT on           ---- 出错回滚
begin tran                  ---- 事务开始
 if exists(select 1 from kc with (xlock) inner join  (select ypID,sum(sl) sl,rkdj from salexm_temp1 group by ypID,rkdj) a on kc.ypID=a.ypID and kc.rkdj=a.rkdj where kc.kcsl<a.sl)  ---这句上的kc表加有排他锁xlock
               begin
               delete salexm_temp1     -----库存不足时清空表salexm_temp1 
               commit tran              ----库存不足时结束事务
               RETURN 1               -----库存不足时返回1
               end
               else
              begin        
              UPDATE [yd].[dbo].[kc] SET kcsl=kc.kcsl-a.sl from kc inner join (select ypID,sum(sl) sl,rkdj from salexm_temp1 group by ypID,rkdj) a on kc.ypID=a.ypID and kc.rkdj=a.rkdj  ---库存满足时改库存
              INSERT INTO [yd].[dbo].[sale]  ( [doctorname], [czyname]) VALUES ( @doctorname,@czyname) 
              select SCOPE_IDENTITY( )  AS djh          ----返回插入表sale时标识列返回的标识值:djh
              UPDATE [yd].[dbo].[salexm_temp1] SET xsdjh =SCOPE_IDENTITY( ) where xsdjh is NULL
                                                       ------用返回的标识值:djh更新表salexm_temp1的xsdjh字段
              INSERT INTO [yd].[dbo].[salexm]  select * from [yd].[dbo].[salexm_temp1] where xsdjh=SCOPE_IDENTITY( )                                
              delete salexm_temp1        -----将salexm_temp1表中的内容插入表salexm中,并清空表salexm_temp1
              commit tran                -----事务结束
              end 
GO
问题1:
    第一条判断库存的语句中在kc表上加的排他锁xlock(这样加锁不知道对不对)是否在整个事务(或存储过程)中都存在,还是第一条语句一结束就释放了kc表的资源。这个kc表上的排他锁是表级锁还是行级锁?如果始终存在,在C/S的并发情况下是否很容易发生死锁?
问题2:
    这个存储过程逻辑上有问题吗?特别是存储过程中那3个返回标识列值的“SCOPE_IDENTITY( )”在整个事务中返回的值是否始终保持一致?
问题3:当salexm_temp1表为空时,第一条判断库存的语句仍然会通过(认为库存满足),并执行else后面的语句,这样只有sale表会插入数据,其余表的内容不变。有什么其他的方法避免这种情况吗(不想在前面在加一条if语句以判断salexm_temp1表是否为空)?

解决方案 »

  1.   

    可以通过设置事务的隔离级别来满足你的要求。默认的隔离级别是read committed
      

  2.   

    我把存储过程改成这样了:
    CREATE PROCEDURE [update_kc_minus]
    (@czyname  [varchar](50),
     @doctorname  [varchar](50)=null,
                  @djh int output)
    AS
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE  ----加了最高的隔离级别SERIALIZABLE
    SET XACT_ABORT on
    begin transaction xs                   ---- 事务开始
     if exists(select 1 from kc with (TABLOCKX) inner join  (select ypID,sum(sl) sl,rkdj from salexm_temp1 group by ypID,rkdj) a on kc.ypID=a.ypID and kc.rkdj=a.rkdj where kc.kcsl<a.sl)  这句上的kc表加有表级排他锁TABLOCKX
                   begin
                   delete salexm_temp1
                   commit transaction xs
                   RETURN 1
                   end
                   else
                  begin        
                  UPDATE [yd].[dbo].[kc] SET kcsl=kc.kcsl-a.sl from kc inner join (select ypID,sum(sl) sl,rkdj from salexm_temp1 group by ypID,rkdj) a on kc.ypID=a.ypID and kc.rkdj=a.rkdj
                  INSERT INTO [yd].[dbo].[sale]  ( [doctorname], [czyname]) VALUES ( @doctorname,@czyname) 
                  select @djh=SCOPE_IDENTITY( ) --返回插入表sale时标识列返回的标识值给output
                  UPDATE [yd].[dbo].[salexm_temp1] SET xsdjh =@djh where xsdjh is NULL
                  INSERT INTO [yd].[dbo].[salexm]  select * from [yd].[dbo].[salexm_temp1] where xsdjh=@djh 
                  delete salexm_temp1
                  commit transaction xs      --事务结束 
                  end
    GO
    请问这样能解决并发问题,保证所有工作站同时调用这个存储过程,但在服务器执行时都是串行的,互不干扰吗?这样会不会发生死锁?