MySQL連續範圍問題

壹頁書發表於2014-12-31
MySQL技術內幕 SQL程式設計 58頁
首先建立實驗表
create table t(a int primary key);
insert into t values(1);
insert into t values(2);
insert into t values(3);
insert into t values(100);
insert into t values(101);
insert into t values(103);
insert into t values(104);
insert into t values(105);
commit;

實驗目標是求數字的連續範圍,以上面的資料為例,應該求得的連續範圍為
1-3
100-101
103-105

第一步,給資料增加行號
select a,@a:=@a+1 rn from t,(select @a:=0) as a;


第二步,求得資料與行號的差值
  1. select a,rn,a-rn diff from
  2. (
  3.     select a,@a:=@a+1 rn from t,(select @a:=0) as a

  4. ) b;


第三步 獲得結果
  1. select min(a) start_range,max(a) end_range
  2. from
  3. (
  4.     select a,rn,a-rn diff from
  5.     (
  6.         select a,@a:=@a+1 rn from t,(select @a:=0) as a
  7.     ) b
  8. ) c group by diff;



用之前的簽到實驗說明這個連續範圍的用法.
http://blog.itpub.net/29254281/viewspace-1379159/

award_chance_history是活動的簽到表,userid記錄使用者的ID,createtime記錄使用者的簽到時間.
實驗求每個使用者的連續簽到時間.
  1. create table award_chance_history
  2. (
  3.     id int primary key auto_increment,
  4.     userid int,
  5.     createtime datetime
  6. );

  7. insert into award_chance_history(userid,createtime)
  8. select ceil(rand()*10000),str_to_date('2014-12-15','%Y-%m-%d')+interval ceil(rand()*10000) minute from nums where id<30;
  1. insert into award_chance_history(userid,createtime)
  2. select userid,createtime + interval ceil(rand()*10) day from award_chance_history,nums
  3. where nums.id <10 order by rand() limit 150;

第一步,獲取使用者每天的簽到情況
獲取使用者簽到時間和現在時間的差值,並且用distinct過濾使用者在一天內多次簽到的記錄
select
distinct
userid,
date_format(createtime,'%Y-%m-%d') createtime,
datediff(createtime,now()) diff
from award_chance_history order by userid,createtime

第二步,模擬分析函式,每個使用者都產生一個自己的序列
select
b.*,
@gid := @cgid,
@cgid := b.userid,
if(@gid = @cgid, @rank := @rank + 1, @rank := 1)  rank,
b.diff-@rank flag from (
    select
    distinct
    userid,
    date_format(createtime,'%Y-%m-%d') createtime,
    datediff(createtime,now()) diff
    from award_chance_history order by userid,createtime
) b, (SELECT @gid := 1, @cgid := 1, @rank := 1) as a

diff和rank序列相減,作為flag欄位,flag相同說明使用者是連續簽到.
如下圖的249使用者,在16,17日連續簽到,他們的flag都是-16.


第三步,根據分組得到連續簽到的結果

  1. select userid,min(c.createtime),max(c.createtime),count(*) from
  2. (
  3.     select
  4.     b.*,
  5.     @gid := @cgid,
  6.     @cgid := b.userid,
  7.     if(@gid = @cgid, @rank := @rank + 1, @rank := 1) rank,
  8.     b.diff-@rank flag from (
  9.         select
  10.         distinct
  11.         userid,
  12.         date_format(createtime,'%Y-%m-%d') createtime,
  13.         datediff(createtime,now()) diff
  14.         from award_chance_history order by userid,createtime
  15.     ) b, (SELECT @gid := 1, @cgid := 1, @rank := 1) as a
  16. ) c group by userid,flag;



第四步,求每個使用者最長的連續簽到時間段
在第三步的基礎上,再模擬一次分析函式

  1. select * from (
  2.     select d.*,
  3.     @ggid := @cggid,
  4.     @cggid := d.userid,
  5.     if(@ggid = @cggid, @grank := @grank + 1, @grank := 1) grank
  6.     from
  7.     (
  8.         select userid,min(c.createtime) begin_date ,max(c.createtime) end_date,count(*) c from
  9.         (
  10.             select
  11.             b.*,
  12.             @gid := @cgid,
  13.             @cgid := b.userid,
  14.             if(@gid = @cgid, @rank := @rank + 1, @rank := 1) rank,
  15.             b.diff-@rank flag from (
  16.                 select
  17.                 distinct
  18.                 userid,
  19.                 date_format(createtime,'%Y-%m-%d') createtime,
  20.                 datediff(createtime,now()) diff
  21.                 from award_chance_history order by userid,createtime
  22.             ) b, (SELECT @gid := 1, @cgid := 1, @rank := 1) as a
  23.         ) c group by userid,flag
  24.         order by userid,count(*) desc
  25.     ) d,(SELECT @ggid := 1, @cggid := 1, @grank := 1) as e
  26. )f
  27. where grank=1;


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

相關文章