随笔-143  评论-68  文章-0  trackbacks-0

项目要求能够重复利用编号值,编号值得范围是 1~65535。也就是,当自动分配的上限超过65535的时候,能够回过头来找那些已经不使用的编号值。有4张表需要重复利用编号值。
 
思路是每张表都做一个 trigger before insert。每次取该表的最大编号+1。如果该编号已经超过65535。那么随机分配一个1~65535的编号,检查是否使用,如果已经有人使用,那么继续随机。尝试次数超过65535时,也就是不太可能再找到合适的编号时,随便插入一个最大编号。编号列有唯一索引,所以此时会出错退出事务。
 
缺陷有几个:
1、随机寻找的编号可能分布不均匀,有可能还是有少数几个可以使用的编号,但是找不到。也就是查找编号的算法需要改进。
    (比如遍历?比如分配的时候采用二分法分配,二分法查找?这样可以均匀分配编号,又可以快速查找)
2、出错的方式不优雅。当编号用满时,是通过唯一索引检查失败之后产生错误。MySQL不能从trigger 中抛出自定义的错误。
 
PS:写完才发现二分法更好,看来算法基础不行。
 
这次任务加深了对 MySQL trigger & procedure的了解。拷贝代码做一个记录~~~
其中一张表的触发器代码:
create trigger SetPiUnderId BEFORE INSERT on t_play_item
for each ROW
    begin
         DECLARE uid int;
         declare usedNum int;
         declare retry int;
         declare maxRetry int;
        
         SELECT max(pi_under_id)+1 into uid from t_play_item;
        
         IF uid = 0
         then
              set uid = 1;
         end if;
        
         IF uid < 65535 THEN
            -- UPDATE t_play_item set pi_under_id = pi_id where pi_id = new.pi_id;
            set NEW.pi_under_id = uid;
         else
             begin
                  -- 否则开始生成随机数查询
                 
                 
                  set maxRetry = 65530;
                  set retry = 0;
                 
                  REPEAT
                 
                      set uid = RAND() * 65530;
                      set usedNum = 0;
                     
                      -- 有几条记录使用这个编号
                      select count(*) into usedNum from t_play_item where pi_under_id = uid;
                      set retry = retry + 1;
                     
                  until usedNum = 0 or retry >= maxRetry
                  end repeat;
                 
                  IF  usedNum = 0
                  then
                      -- UPDATE t_play_item set pi_under_id = uid where pi_id = new.pi_id;
                      set NEW.pi_under_id = uid;
                  else
                      -- 强制设置成id, 没有找到抛出错误的办法,
                      set NEW.pi_under_id = uid;
                      -- UPDATE t_play_item set pi_under_id = pi_id where pi_id = new.pi_id;
                      -- UPDATE t_play_item set pi_under_id = 65535 where pi_id = new.pi_id;
                  end if;
                 
             end;   
         end if;
    end
 
 
 
用来测试的存储过程:
CREATE PROCEDURE TestSetPiUnderId(in times int, in showMsg bool)
    MODIFIES SQL DATA
begin
     declare t int;
     set t = 0;
     REPEAT
           set t = t + 1;
           insert into t_play_item(pi_style, pi_rect_no, pi_sent, pi_des) values(t, 1, 0, '');
          
           IF showMsg then
              select CONCAT('插入成功第', CAST(t as char));
           end if;
     until t> times
     end repeat;
end;
posted on 2008-04-23 17:04 windone 阅读(2378) 评论(0)  编辑 收藏 引用 所属分类: 数据库
只有注册用户登录后才能发表评论。