获取连续登陆天数,连续签到天数,类似这样的需求应该是一个常见的需求,那么我们有没有一套成熟的解决方案呢 ?下面我来跟大家分享一下我的故事。
在猴年马月的一天,有个用户反馈个人中心打开缓慢,需要7、8秒,做为一个认真负责任的程序员GG,我尼玛放下手中的其他工作,跟踪调查并且解决该问题。
第一步重现问题:
连着登陆了好几个账号到个人中心,打开都不慢呀 ,那是什么问题呢,就你一个人出问题,是你人品差吧,正当打算以此敷衍用户的时候,测试组的小陶说,他也遇到了这个问题,纳尼 !!你个人品差的家伙 ,让俺来看看,你要重现不了,看我不打死你。 于是他登陆账号试了一下,果然是有些缓慢,大概在4、5秒的样子,嗯,至此,这个问题重现成功了 。嗯,厉害 。
第二步查找问题根本原因:
总不是这两个人人品都差吧,经过一番严密的调查,问题最终锁定在获取连续签到次数上,别问我怎么查到的,因为这两个奇葩居然做到连续签到近100天的牛逼战绩,然后获取连续签到的存储过程采用的循环算法是连续登陆天数越多,算法复杂度就越高。下面贴存储过程代码:
1 --循环法
declare @day int = 1, --2 @userId int =1, --用户id
3 @count int = 0 , --连续签到多少天
4 @isSinginToday int --今天是否签到
5
6 while exists ( select * from#SignInLog7 where UserId = @UserId and DATEDIFF(day ,createtime ,getdate() ) = @day)8 begin
9 set @count = @count + 1 --【循环方法】
10 set @day = @day + 1 --
11 end
12
13 select @isSinginToday =COUNT(*) from #SignInLog where UserId = @UserId and DATEDIFF(day ,createtime ,getdate() ) = 0 --今天是否登录
14
15
16 select @isSinginToday , --当天是否签到
17 @count + @isSinginToday --连续签到n天
把表结构也贴出来吧,省得你们这些懒人造数据麻烦,检验代码麻烦
--用户签到日志表
create table #SignInLog ( UserId int, --用户id
CreateTime datetime )--签到时间
insert into #SignInLog values(1,'20160924'),
(1,'20160923'),
(1,'20160922'),
(1,'20160921'),
(1,'20160919'),
(2,'20160924'),
(2,'20160923'),
(2,'20160920' )
可以看到这个存储过程采用循环的方法,去检查前一天是否签到,有的话继续查前一天,并把用于统计连续签到天数的计数器加一 ,前一天没有签到的话作为退出循环的条件,真是段思路清晰的好代码。但是随着连续次数的增多,select语句的执行次数也会增多,所以才会出现了那些连续签到天数多的人缓慢,连续签到天数少的人正常的情况 。 嗯....... 原因也找到了。喝杯茶压压惊先。
最后一步了,也是最难的,解决问题:
这代码也没毛病,不换个思路的话,问题应该也得不到解决。正当我绞尽脑汁的时候,我有点想上厕所了,正当我上厕所的时候,灵感来了,(为了有意让灵感发生在洗手间,容易吗我)一个sql的函数映入我的眼帘,rownumber() 。于是最后给这个方法取的名字就叫rownumber法。看代码:
--【row_number 法 】
declare @now datetime = getdate() ,@count int,@userid int = 1,@isSinginToday int
select @count = count(*) from(select datediff( day , CreateTime , @now ) aa , --签到时间对比今天的差值
row_number() over (order by createtime desc ) bb --排序字段
from#SignInLogwhere UserId = @userId and datediff( day , CreateTime , @now ) > 0 --条件排除今天的签到记录
) T where aa =bbselect @isSinginToday =COUNT(*) from #SignInLog where UserId = @UserId and DATEDIFF(day ,createtime ,getdate() ) = 0 --今天是否登录
select @isSinginToday , --当天是否签到
@count + @isSinginToday --连续签到n天
这代码思路也算清晰的, 同学们,不知道你们看完这个故事,学到了没。