Session重疊問題學習(四)--再優化

壹頁書發表於2018-01-18
接前文:
需求描述和第一版解決方案(執行時間90秒)
http://blog.itpub.net/29254281/viewspace-2150229/

優化和修改bug的版本(執行時間25秒)
http://blog.itpub.net/29254281/viewspace-2150259/


我覺得在集合思維處理方式中,前文已經達到最優了.

如果放棄完全的集合處理思維,實際上還可以更加的優化.

前文的幾個問題.
1.引入了過多的表結構.
2.寫表本身也花費了時間.
3.前文按天批處理,粒度還是細了.應該一把批量全出最快.
4.前文計算最小間隔範圍的部分,因為應用集合化思維,不好理解效能還差.

前文計算最小間隔範圍的部分如下
  1. select roomid,CAST(starttime as DATETIME) starttime,CAST(endtime as DATETIME) endtime from (    
  2.      select @d as starttime,@d:=d,v3.roomid,v3.d endtime from (    
  3.                 select distinct roomid,     
  4.                 case        
  5.                 when nums.id=1 then v1s       
  6.                 when nums.id=2 then v1e       
  7.                 when nums.id=3 then v2s       
  8.                 when nums.id=4 then v2e       
  9.                 end d   from (    
  10.                     select   v1.roomid, v1.s v1s,v1.e v1e,v2.s v2s,v2.e v2e    
  11.                     from t1 v1    
  12.                     inner join t1 v2 on ((v1.s between v2.s and v2.e or v1.e between v2.s and v2.e )  and v1.roomid=v2.roomid)     
  13.                     where v2.roomid in(select distinct roomid from t1 where date(s)=pTime)    
  14.                     and v2.s>=pTime and v2.s<(pTime+interval '1' dayand (v2.roomid,v2.userid,v2.s,v2.e)!= (v1.roomid,v1.userid,v1.s,v1.e)     
  15.                 ) a,nums where nums.id<=4    
  16.                 order by roomid,d    
  17.     ) v3,(select @d:='') vars    
  18. ) v4 where starttime!='' 

該部分使用集合處理方式,不好理解效能還差.

這塊可以通過遊標寫臨時表輕易解決。
本質上最小範圍就是
每天每個房間每個記錄的開始時間和結束時間都扣出來作為一行 排序。

然後找到每個時間最近的下一個時間,作為最小時間範圍.

如果使用遊標,遍歷一遍即可.

  1. DELIMITER $$  
  2.   
  3. CREATE DEFINER=`root`@`localhost` PROCEDURE `p`()  
  4. BEGIN  
  5.     declare done int default 0;      
  6.     declare v_roomid bigint;  
  7.     declare v_start timestamp;  
  8.     declare v_end timestamp;  
  9.     declare cur_test CURSOR for select roomid,s,e from t1  ;  
  10.   
  11.     DECLARE  CONTINUE HANDLER FOR NOT FOUND  SET done = 1;      
  12.       
  13.     drop table if exists t1;  
  14.     drop table if exists tmp_time_point;  
  15.     CREATE temporary TABLE `t1` (  
  16.       `roomid` int(11) NOT NULL DEFAULT '0',  
  17.       `userid` bigint(20) NOT NULL DEFAULT '0',  
  18.       `s` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,  
  19.       `e` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',  
  20.       primary KEY `roomid` (`roomid`,`s`,`e`,`userid`)  
  21.     ) ENGINE=InnoDB;  
  22.   
  23.     create temporary table tmp_time_point(  
  24.             roomid bigint,  
  25.             timepoint timestamp,  
  26.             primary key(roomid,timepoint)  
  27.     ) engine=memory;  
  28.   
  29.   
  30. insert into t1  
  31. select distinct    
  32. roomid,    
  33. userid,    
  34. if(date(s)!=date(e) and id>1,date(s+interval id-1 day),s) s,    
  35. if(date(s+interval id-1 day)=date(e) ,e,date_format(s+interval id-1 day,'%Y-%m-%d 23:59:59')) e    
  36. from (    
  37. SELECT DISTINCT s.roomid, s.userid, s.s, (      
  38.         SELECT MIN(e)      
  39.         FROM (SELECT DISTINCT roomid, userid, roomend AS e      
  40.             FROM u_room_log a      
  41.             WHERE NOT EXISTS (SELECT *      
  42.                 FROM u_room_log b      
  43.                 WHERE a.roomid = b.roomid      
  44.                     AND a.userid = b.userid      
  45.                     AND a.roomend >= b.roomstart      
  46.                     AND a.roomend < b.roomend)      
  47.             ) s2      
  48.         WHERE s2.e > s.s      
  49.             AND s.roomid = s2.roomid      
  50.             AND s.userid = s2.userid      
  51.         ) AS e      
  52. FROM (SELECT DISTINCT roomid, userid, roomstart AS s      
  53.     FROM u_room_log a      
  54.     WHERE NOT EXISTS (SELECT *      
  55.         FROM u_room_log b      
  56.         WHERE a.roomid = b.roomid      
  57.             AND a.userid = b.userid      
  58.             AND a.roomstart > b.roomstart      
  59.             AND a.roomstart <= b.roomend)      
  60.     ) s, (SELECT DISTINCT roomid, userid, roomend AS e      
  61.     FROM u_room_log a      
  62.     WHERE NOT EXISTS (SELECT *      
  63.         FROM u_room_log b      
  64.         WHERE a.roomid = b.roomid      
  65.             AND a.userid = b.userid      
  66.             AND a.roomend >= b.roomstart      
  67.             AND a.roomend < b.roomend)      
  68.     ) e      
  69. WHERE s.roomid = e.roomid      
  70.     AND s.userid = e.userid     
  71.     
  72. ) t1 ,    
  73. nums     
  74. where  nums.id<=datediff(e,s)+1    
  75. ;    
  76.   
  77.     open cur_test;      
  78.     repeat      
  79.         fetch cur_test into v_roomid, v_start,v_end;      
  80.         if done !=1 then    
  81.            insert ignore into tmp_time_point(roomid,timepoint) values(v_roomid,v_start);  
  82.            insert ignore into tmp_time_point(roomid,timepoint) values(v_roomid,v_end);  
  83.         end if;    
  84.     until done end repeat;      
  85.     close cur_test;   
  86.       
  87.     
  88.   
  89.    
  90. select roomid,date(s) dt,round(sum(timestampdiff(second,s,e))/60) ts,max(c) c from (       
  91.     select roomid,s,e ,count(distinct userid) c  from (  
  92.         select distinct v6.roomid,v6.userid,greatest(s,starttime) s,least(e,endtime) e  
  93.         from (  
  94.             select distinct roomid,CAST(starttime as DATETIME) starttime,CAST(endtime as DATETIME) endtime from (  
  95.                 select   
  96.                 if(@roomid=roomid,@d,'')  as starttime,@d:=timepoint,@roomid:=roomid,p.roomid,p.timepoint endtime  
  97.                 from tmp_time_point p,(select @d:='',@roomid:=-1) vars  
  98.                 order by roomid,timepoint  
  99.             ) v4 where starttime!='' and date(starttime)=date(endtime)  
  100.         ) v5 inner join t1 v6 on(v5.starttime between v6.s and v6.e and v5.endtime between v6.s and v6.e and v5.roomid=v6.roomid)  
  101.     ) v6 group by roomid,s,e having count(distinct userid)>1     
  102. ) v7 group by roomid,date(s);  
  103.   
  104.   
  105. END  

都內聚到一個過程之後,不需要建立額外的普通表,直接在過程中建立臨時表.實現高內聚,低耦合.

call p 
過程返回的結果即為最終結果.

三次測試耗時均低於 10.3秒



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-2150297/,如需轉載,請註明出處,否則將追究法律責任。

相關文章