SQL实用题型-用户最长连续登陆天数

SQL-查询用户最长连续登陆天数

    • sql经典题型:求用户连续登陆的最长天数
    • 题目难点:如何求连续登陆的天数
    • 本题的思考

sql经典题型:求用户连续登陆的最长天数

已知:一个简单的用户登陆表:

uid dat
u1 2019/9/1
u1 2019/9/2
u1 2019/9/3
u1 2019/9/8
u1 2019/9/9
u2 2019/9/2
u2 2019/9/3

想要得到:查询结果如下

uid max_day
u1 3
u1 2
u2 2

题目难点:如何求连续登陆的天数

  1. 首先想到的是用 COUNT + GROUP BY 的用法,那么GROUP BY后面用于分组的条件就是连续登陆的标记(分别为连续登陆2天,3天,6天,10 天等等)
  2. 如何对于每一个用户构建连续登陆的标记? 这个时候,很容易就会想到平时算法中的相邻循环加1,但是在SQL中一定要转变这种思维方式,而是思考能不能给这些连续加1的日期打上一个相同的标签,这样子就可以利用这个标签当作GROUP BY分组的条件了。
  3. 用户U1为例,该用户有两次连续登陆的经历,第一次是连续3天,第二次是连续2天,我们只要能对这两次的连续登陆行为打上两个标签,则可以使用GROUP BY对于不同标签组内的数据进行COUNT的计算,从而得到用户的连续登陆天数这一新变量。
  4. 用户U1连续登陆3天的3个数据为例,怎么给着三个数据打上同样的标签呢?此时我们思考,这三个数据DATE1,DATE2,DATE3的关系是数值上依次加1,加入我们可以生成一列同样依次加1的数据R1,R2,R3(如1,2,3),那么DATE1-R1的差值就会等于DATE2-R2同时等于DATE3-R3,那么我们就可以使用这个差值作为一个连续登陆的标记,所以同样对于用户U1连续登陆2天的2个数据,我们也用该种方法进行连续登陆的标记
  5. 这个时候我们需要思考一个问题,如果两次做差的数值 A,B是相同的应该怎么办?这样的话,就会把原来分开的两次连续登陆(3天标签A,2天标签B)计算成一次的连续登陆(5天标签A=标签B)。此时我们应该想到,连续登陆的3天和2天的日期一定不是相邻的,是跳跃的,否则就会记做一次登陆5天。那么只要我们生成一串长长的自然数列(1,2,3,4,5,6…)刚好依次对应与用户登陆的日期,然后我们使用这个自然数列和用户登陆的日期做差,这样就一定能得到两组标签了。这里再解释一遍因为如果是连续登陆的话,用户的日期是连续的(d1,d2,d3),我们使用自然数列(1,2,3)与它做差,得到的数字是相同的数字d1-1=d2-2=d3-3=A,可以用作一次连续登陆行为的标签,当用户隔了几天之后再次出现连续登陆行为(d4,d5)时,重新开始连续登陆日期数字已经跳到几天之后(即d4与d3不再是加1的关系),但是与该日期(d4,d5)依次对应的自然数列(4,5)与之前的(1,2,3)还是相邻的关系。这样的话d4-4=d5-5=B 且不等于 d3-3=A这样我们就给d4和d5建立了一个新的标签。所以我们连续登陆3天和2天的日期后面对应的标签是两个不同的数字A,B
  6. 那么如何实现生成一串自然数列的功能呢,当然是使用我们的ROW_NUMBER() 函数啦,因为我们是对每一个用户进行如上的操作,所以OVER() 中的条件应该是PARTITION BY uid, 同时题目没有说日期变量是有序的,所以我们还应该加上ORDER BY dat 这一条件。
  7. 故实现该连续登陆的标记部分的查询语句应该是:
SELECT uid, dat, (DAY(dat) - ROW_NUMBER() OVER(PARTITION BY uid ORDER BY dat)) AS day_label
FROM table
  1. 故计算连续登陆天数day_count的语句是:
SELECT uid,day_label,count(*) as day_count
FROM (
    SELECT uid, dat, (DAY(dat) - ROW_NUMBER() OVER(PARTITION BY uid ORDER BY dat)) AS day_label FROM table)
GROUP BY uid,day_label
  1. 故求最大连续登陆天数max(day_count)的语句是:
SELECT uid,max(day_count) as max_day
FROM
   ( SELECT uid,day_label,count(*) as day_count
        FROM (
             SELECT uid, dat, (DAY(dat) - ROW_NUMBER() OVER(PARTITION BY uid ORDER BY dat)) AS day_label FROM table)
GROUP BY uid,day_label)
GROUP BY uid

9.最后再解释一下代码,共三层嵌套子查询,最内层使用开窗函数给每一批连续登陆的日期都打上每一批对应的day_label标签,第二层子查询是利用上一层的标签作为GROUP BY分类计数的类别,这一层的查询是得到了用户每一次连续登陆对应的天数。第三层的子查询就是使用max函数与GROUP BY函数取出每一个用户对应的最长登陆天数。

本题的思考

  1. 分析题意,搞清楚题目的逻辑顺序,先求连续登陆天数,再取最大
  2. 思维方式要使用SQL中的归类思想,创建一组变量,使得它们能够作为分类的标签。
  3. 思考细节,日期是否需要排序,顺序还是倒序
  4. 熟悉开窗函数的应用

你可能感兴趣的:(SQL实用题型整理)