以下存储过程是为了避免不同客户端同时读到同一条记录而写的。但存在一个bug就是:insert 操作已经成功执行了,但是有可能是update没有成功执行,导致其他人在别人取出来的几分钟后还是可以取到记录(少部分数据会这样)。比如有条id=123的资料已经给了A用户,按道理不管如何其他人都拿不到的。
但是过了几份钟后,在B用户那里竟然可以显示到id=123这个资料。
DELIMITER $$ DROP PROCEDURE IF EXISTS `crm`.`SelectCustomerData`$$ CREATE DEFINER=`root`@`localhost` PROCEDURE `SelectCustomerData`(Workid char(20)) 
begin 
-- --------------------------------- 
declare SQLCondition varchar(100); 
declare Cust_id int default -1; 
set SQLCondition=concat(" ifdealing=0 and staffno='' and workid='",Workid,"'"); 
-- 这里条件的意思是:查询属于workid组的未处理(ifdealing=0)并且工号字段为空的数据    
-- 顺序取记录 SET  TRANSACTION  ISOLATION  LEVEL  SERIALIZABLE ; --设置事务为串读 
START TRANSACTION ;  --开始一个事物select id into Cust_id from hr_customerdata where callstate='3';---首先取callstate=‘3’的资料,如果没有再执行以下的SQL;
if Cust_id=-1 then 
select id into Cust_id from hr_customerdata where ifdealing=0 and staffno='' and workid=Workid order by id limit 1 for update; 
end if;if (Cust_id<>-1) then --如果能够取到数据,则更新状态,并把数据取出来后由存储过程返回给客户端程序。 
  insert into TopHis value(Cust_id);----如果这条记录被提取过了,就在这个表中插入一条数据;
  update hr_customerdata set ifdealing=1 where id= Cust_id ; 
end if; 
 set @sql=concat("SELECT * from hr_customerdata where id=",Cust_id); 
  PREPARE stmt1 FROM @sql ; 
  EXECUTE stmt1 ; 
  DEALLOCATE PREPARE stmt1; 
--Set@sql=NULL; 
 
commit ; 
end$$ DELIMITER $$;

解决方案 »

  1.   

    或者 

    declare not found  ... set aa=1aa=0
    update hr_customerdata set ifdealing=1 where id= Cust_id and ifdealing<>1;
    if  aa=1 then  -- 没有更新
        中断程序
    endif 
        
      

  2.   

    哈哈,我现在只想到这种方法了。不过这个我后来检查了,只是个别的。
    trainee 能否提供些资料参考下?
    数据库只有hr_customerdata 为InnoDB表,其他的都是myisam。
      

  3.   

    假如按以上的方法
    根本就不需要前面的select for update 锁定, 甚至不用start transaction
    因为update 语句有内置的锁定, 不知mysql是不是这样的.你前面的select for update锁定没有任何作用,因为锁的是过时的数据.