獲取連續登陸天數,連續簽到天數,類似這樣的需求應該是一個常見的需求,那么我們有沒有一套成熟的解決方案呢 ?下面我來跟大家分享一下我的故事。
在猴年馬月的一天,有個用戶反饋個人中心打開緩慢,需要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 #SignInLog 7 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 #SignInLog where UserId = @userId and datediff( day , CreateTime , @now ) > 0 --條件排除今天的簽到記錄 ) T where aa = bb select @isSinginToday =COUNT(*) from #SignInLog where UserId = @UserId and DATEDIFF(day ,createtime ,getdate() ) = 0 --今天是否登錄 select @isSinginToday , --當天是否簽到 @count + @isSinginToday -- 連續簽到n天
這代碼思路也算清晰的, 同學們,不知道你們看完這個故事,學到了沒。