程序员人生 网站导航

mysql中类似oracle的over分组实现

栏目:数据库应用时间:2015-02-06 09:18:21

今天,看到他人问问题,需求大概是这样的。

id s 开始时间 结束时间
1 20 2001-01-01 08:10:20 2001-01-01 08:10:40
1 9 2001-01-01 08:10:41 2001-01-01 08:10:50
1 60 2001-01-01 08:10:51 2001-01-01 08:11:51
1 2 2001-01-01 08:12:51 2001-01-01 08:12:53
2 51 2001-01-01 08:10:00 2001-01-01 08:10:51
2 60 2001-01-01 08:11:00 2001-01-01 08:12:00
2 5 2001-01-01 08:13:00 2001-01-01 08:13:05
2 15 2001-01-01 08:13:06 2001-01-01 08:13:21
2 5 2001-01-01 08:13:25 2001-01-01 08:13:30
要统计用户相同,时间连续(也就是结束时间和下1次的开始时间相差1秒)的结果,终究显现

id,总的时间间隔,这个时间段的开始时间,这个时间段的结束时间。

如上面id=1,出来结果应当是1,

1 89 2001-01-01 08:10:20 2001-01-01 08:11:51
1 2 2001-01-01 08:12:51 2001-01-01 08:12:53
=============================================================================

对上面这个需求,如果用oracle,那末应当比较好实现。用group by,over,lag的方式很轻松就可以弄定。但对mysql,似乎统计函数比较少。

本来对MySQL复杂的SQL利用也不算熟习。因而试着写了写。

1、我自己整理了1下思路,第1步目标:

1、需要整理出1个唯1字段分组

2、需要在下1条记录显示上1条记录的结束时间


2、根据第1步整理的目标

1、对第1个小目标分解

1)首先每行的唯1行号,这是构成唯1字段分组可以借用的。

2)标记位要能够辨别不同用户,比如上1个用户的结束时间和下1个用户的开始时间恰好连了起来,要能辨别出是两个用户。

2、第2个小目标分解

        1)把时间转化为数字或字符,去掉没必要要字符,这样便于后续处理


3、创建测试

1、添加表

create table time_log( id int, --用户id s int, --时间间隔 start_t varchar(20), --开始时间 end_t varchar(20) --结束时间 )
2、添加测试数据

insert into time_log(id,s,start_t,end_t) values(1,20,'2001-01-01 08:10:20','2001-01-01 08:10:40'); insert into time_log(id,s,start_t,end_t) values(1,9,'2001-01-01 08:10:41','2001-01-01 08:10:50'); insert into time_log(id,s,start_t,end_t) values(1,60,'2001-01-01 08:10:51','2001-01-01 08:11:51'); insert into time_log(id,s,start_t,end_t) values(1,2,'2001-01-01 08:12:51','2001-01-01 08:12:53'); insert into time_log(id,s,start_t,end_t) values(2,51,'2001-01-01 08:10:00','2001-01-01 08:10:51'); insert into time_log(id,s,start_t,end_t) values(2,60,'2001-01-01 08:11:00','2001-01-01 08:12:00'); insert into time_log(id,s,start_t,end_t) values(2,5,'2001-01-01 08:13:00','2001-01-01 08:13:05'); insert into time_log(id,s,start_t,end_t) values(2,15,'2001-01-01 08:13:06','2001-01-01 08:13:21'); insert into time_log(id,s,start_t,end_t) values(2,5,'2001-01-01 08:13:25','2001-01-01 08:13:30');

3、SQL

1)根据第1步目标

出来SQL

select @rownum:=@rownum+1 as rownum,@preEndTime as preendnum,@preEndTime:=dendnum ,t.* from ( select t.* ,CONCAT(id,'-',date_format(str_to_date(t.START_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s'))as dstartnum ,CONCAT(id,'-',date_format(str_to_date(t.end_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s')+1) as dendnum ,date_format(str_to_date(t.START_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s') istart ,date_format(str_to_date(t.end_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s') iend from time_log t ) t,(SELECT @preEndTime:='',@rownum:=0) r

1)根据出来的列整理,生成id,标记连续

select t.*,case when preendnum=dstartnum then 0 else rownum end as di from ( select @rownum:=@rownum+1 as rownum,@preEndTime as preendnum,@preEndTime:=dendnum ,t.* from ( select t.* ,CONCAT(id,'-',date_format(str_to_date(t.START_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s'))as dstartnum ,CONCAT(id,'-',date_format(str_to_date(t.end_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s')+1) as dendnum ,date_format(str_to_date(t.START_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s') istart ,date_format(str_to_date(t.end_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s') iend from time_log t ) t,(SELECT @preEndTime:='',@rownum:=0) r ) t


2)终究1步步处理,出来终究SQL

select id,s_nums 时间s ,str_to_date(istarttimes,'%Y-%m-%d %h:%i:%s') as 开始时间 ,end_t as 结束时间 from ( select case when @knum=dirow then 0 else dirow end as flag,@knum:=dirow,t.* from ( select * from ( select t.*,date_sub(end_t, interval totals day_second) as istarttimes from ( select t.*,@rowid:=@rowid+di as dirow,@sums:=case when di=0 then @sums+s+1 else s end as totals ,@sums2:=case when di=0 then @sums2+s+0 else s end as s_nums from ( select t.*,case when preendnum=dstartnum then 0 else rownum end as di from ( select @rownum:=@rownum+1 as rownum,@preEndTime as preendnum,@preEndTime:=dendnum ,t.* from ( select t.* ,CONCAT(id,'-',date_format(str_to_date(t.START_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s'))as dstartnum ,CONCAT(id,'-',date_format(str_to_date(t.end_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s')+1) as dendnum ,date_format(str_to_date(t.START_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s') istart ,date_format(str_to_date(t.end_T,'%Y-%m-%d %h:%i:%s'),'%Y%m%d%h%i%s') iend from time_log t ) t,(SELECT @preEndTime:='',@rownum:=0) r ) t ) t,(SELECT @rowid:=0) r ) t ) t order by rownum desc ) t,(SELECT @knum:=⑴) r ) t where t.flag<> 0 order by rownum

sql没有大量注释,但1层层剥离,应当很容易理解,这也没有优化。如果在项目开发中让我选择,我肯定用存储进程。




------分隔线----------------------------
------分隔线----------------------------

最新技术推荐