求連續操作(登錄)數量(次數)最大的記錄(用戶)


昨晚上老同事聚會,一個同事說道一個面試問題沒有一個人做出來,就是求連續日期登錄次數最大的用戶,同事說借助 rownumber即可求解,由於是喝酒聊天,也沒有說詳細的解決過程。今天早上想了下,終於想到了具體的解決思路。

登錄時間里面有詳細的時分秒數據,而我們的題目只要求連續的天數,所以使用DATEDIFF函數可以解決,

DATEDIFF(d,LoginTime,getdate()) as diffDate ,

有多個用戶都在登錄,因此應該以用戶名為分區,登錄時間為順序來計算rownumber,因此,就是下面的表達式:

ROW_NUMBER() over(partition by Name order by LoginTime desc) as rn

關鍵問題來了,如何求得連續的登錄情況?

如果是連續的記錄,那么 diffDate- rn 肯定是相同的!

OK,果然這種方式很巧妙,那么我們最終的SQL寫出來也不難了。

開始動手,先構造一個表,插入初始數據:

/*
  求連續登錄次數最多的用戶
*/
create table UserLoginInfo(
  ID int IDENTITY primary key,
  Name varchar(50) not null,
  LoginTime datetime not null
  )
  go
  
  insert UserLoginInfo (Name,LoginTime)
  values('zhang','2015-11-10 12:01:50')
  ,('li','2015-11-11 11:01:50')
  ,('wang','2015-11-9 11:01:50');
  go
  
  insert UserLoginInfo (Name,LoginTime) values 
  ('zhang','2015-11-11 12:01:50'),
  ('li','2015-11-11 12:01:50'),
  ('wang','2015-11-11 11:01:50'),
  
  ('zhang','2015-11-12 12:01:50'),
  ('li','2015-11-13 13:01:50'),
  ('wang','2015-11-12 11:01:50'),
  
  ('zhang','2015-11-13 12:01:50'),
  ('li','2015-11-14 11:01:50'),
  ('wang','2015-11-14 11:01:50');
  go

然后用下面的SQL得到結果:

select top 1 
       Name,diffRn,COUNT(diffRn)as LoginCount 
from(
    select Name,diffDate,rn, (diffDate-rn) as diffRn 
    from(      
          select  ID,Name, 
                  DATEDIFF(d,LoginTime,getdate()) as diffDate , 
                  ROW_NUMBER() over(partition by Name order by LoginTime desc) as rn
          from UserLoginInfo
    ) t1
) t2
group by diffRn,Name 
order by LoginCount desc

答案是:

Name    diffRn    LoginCount
zhang    14    4

 

如果注釋掉 top 1,我們就知道這個結果的由來了:

Name    diffRn    LoginCount
zhang    14    4
li    13    3
wang    14    2
wang    15    1
li    14    1
wang    13    1

 

這個問題也可以衍生出 求連續登錄的用戶,或者求連續登錄15天的用戶(比如QQ的簽到功能),是不是很熟悉呢?

 

實際上,上面這個查詢,遇到一天登錄多次的情況下,統計是不准確的,例如,構造下面的測試數據:

insert UserLoginInfo (Name,LoginTime) values 
     ('zhang'    ,'2015-11-10 12:01:50')
    ,('li'            ,'2013-10-05 11:01:50')
    ,('li'            ,'2013-10-06 11:01:50')
    ,('li'            ,'2014-10-05 11:01:50')
    ,('li'            ,'2014-10-06 11:01:50')
    ,('li'            ,'2015-10-05 11:01:50')
    ,('li'            ,'2015-10-06 11:01:50')
    ,('li'            ,'2015-11-10 11:01:50')
    ,('li'            ,'2015-11-11 11:01:50')
    ,('wang'        ,'2015-11-09 11:01:50')
    ,('zhang'        ,'2015-11-11 12:01:50')
    ,('li'            ,'2015-11-11 12:01:50')
    ,('wang'        ,'2015-11-11 11:01:50')
    ,('zhang'        ,'2015-11-12 12:01:50')
    ,('li'            ,'2015-11-13 13:01:50')
    ,('wang'        ,'2015-11-12 11:01:50')
    ,('zhang'        ,'2015-11-13 12:01:50')
    ,('li'            ,'2015-11-14 11:01:50')
    ,('wang'        ,'2015-11-14 11:01:50')
    ;
View Code

這時應該先去除某天的重復數據,才是正確的,所以查詢應該做如下改進:

select --top 1 
       Name,diffRn,COUNT(diffRn)as LoginCount 
from(
    select Name,diffDate,rn, (diffDate-rn) as diffRn 
    from(      
          select  Name, 
                  diffDate,
                  ROW_NUMBER() over(partition by Name order by diffDate asc) rn
          from ( 
                select distinct Name,DATEDIFF(d,LoginTime,getdate()) as diffDate 
                from  UserLoginInfo 
          ) t0
    ) t1
) t2
group by diffRn,Name 
order by LoginCount desc;

結果是:

Name    diffRn    LoginCount
zhang    14    4
wang    14    2
li    13    2
li    14    2
li    48    2
li    411    2
li    774    2
wang    13    1
wang    15    1

結果符合我們的預期,算是完整的答案了。

不知道別的同學還沒有更好的解決方案?

-------------------------------------------

PS:如果你經常會在程序中寫這樣復雜的SQL,推薦你使用PDF.NET SOD框架SQL-MAP功能,將SQL寫在配置文件中,集中管理,並且方便跨數據庫移植。

SOD框架 PDF.NET_SOD Ver 5.3.6.1125 已經發布,喜歡的朋友可以下載當前這個最新的穩定版本,有問題,可以加框架的QQ群:PDF.NET SOD高級群 18215717

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM