獲取連續登錄天數,連續簽到天數,相似這樣的需求應該是一個常見的需求,那麼咱們有沒有一套成熟的解決方案呢 ?下面我來跟你們分享一下個人故事。程序員
在猴年馬月的一天,有個用戶反饋我的中心打開緩慢,須要七、8秒,作爲一個認真負責任的程序員GG,我尼瑪放下手中的其餘工做,跟蹤調查而且解決該問題。算法
連着登錄了好幾個帳號到我的中心,打開都不慢呀 ,那是什麼問題呢,就你一我的出問題,是你人品差吧,正當打算以此敷衍用戶的時候,測試組的小陶說,他也遇到了這個問題,納尼 !!你我的品差的傢伙 ,讓俺來看看,你要重現不了,看我不打死你。 因而他登錄帳號試了一下,果真是有些緩慢,大概在四、5秒的樣子,嗯,至此,這個問題重現成功了 。嗯,厲害 。sql
總不是這兩我的人品都差吧,通過一番嚴密的調查,問題最終鎖定在獲取連續簽到次數上,別問我怎麼查到的,由於這兩個奇葩竟然作到連續簽到近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語句的執行次數也會增多,因此纔會出現了那些連續簽到天數多的人緩慢,連續簽到天數少的人正常的狀況 。 嗯....... 緣由也找到了。喝杯茶壓壓驚先。spa
這代碼也沒毛病,不換個思路的話,問題應該也得不到解決。正當我絞盡腦汁的時候,我有點想上廁所了,正當我上廁所的時候,靈感來了,(爲了有意讓靈感發生在洗手間,容易嗎我)一個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天
這代碼思路也算清晰的, 同窗們,不知道大家看完這個故事,學到了沒。code