SQL-统计连续N天登陆的用户

解题思路

  1. 用户每天登录的次数可能不止一次,因此先将用户每天登录的日期去重
  2. 然后将数据按用户ID分组,按照登录日期(yyyy-mm-dd)排序rn;
  3. 计算登录日期-rn(第二步的排序值) as diff_date – 用户连续登录的情况下,每次相减的结果都相同
  4. 按照user_id, diff_date 分组,统计count(*)>N的个数,即得到最终的结果。

代码实现

select
    user_id
from(
    select
        user_id, login_date,
        date_sub(login_date, rn) diff_date  -- 第三步
    from(
        select
            user_id, login_date,
            row_number() over(partition by user_id order by login_date) rn  -- 第二步
        from(select distinct user_id, login_date from login_info)t  -- 第一步
        )tt
    )ttt
group by user_id, diff_date
having count(*)>N
<think>嗯,用户想用SQL查询连续N登录的用户数。这个问题我之前在参考资料里看到过,得仔细想想怎么解决。首先,用户提供的几个引用中提到了不同的方法,但核心思路应该类似。比如,引用2和3都提到了使用row_number()窗口函数来为每个用户的登录日期排序,然后通过日期减去排序后的行号得到一个差值,如果这个差值相同,说明是连续的。 那具体怎么做呢?首先,用户可能有重复的登录记录,比如同一多次登录,所以第一步应该是去重,只保留每个用户的登录日期。这一点在引用2和3里都有提到,用distinct或者子查询来去重。然后,按用户分组,按日期排序,给每个用户的登录日期加上行号rn。接下来,用登录日期减去rn的数,得到一个diff_date。如果是连续登录的话,这个diff_date应该相同,因为每增加一,行号也加1,所以差值保持不变。 举个例子,假设用户A在1号、2号、3号登录,行号rn是1、2、3。那么1-1=0,2-2=0,3-3=0,diff_date都是0。这时候分组后统计每个用户和diff_date的组合,数量超过N的就是连续N登录的用户。 不过,这里需要注意日期格式的问题。比如,如果日期是datetime类型,可能需要转换为date类型。另外,不同的数据库系统处理日期加减的方式可能不同,比如在MySQL中使用date_sub函数,而其他数据库可能有不同的语法。 然后,用户的问题是要统计连续N用户数,所以最后的having count(*) >= N。比如连续7的话,N就是7。但用户可能需要参数化这个值,根据不同的需求调整。 还有,引用3中的例子使用了date_sub(login_date, interval rn day),这可能是在MySQL中的写法。不同的数据库可能需要调整日期计算的语法,比如在PostgreSQL中可能是login_date - rn * interval '1 day',但整体思路是一样的。 总结步骤的话,应该是: 1. 去重,得到每个用户的登录记录。 2. 对每个用户的登录日期排序,生成行号rn。 3. 计算login_date - rn,得到diff_date。 4. 按user_id和diff_date分组,统计每组的记录数,筛选出记录数>=N的组。 5. 最后统计这些用户的数量或者列出用户ID。 可能还需要考虑跨月的情况,比如用户从11月30号登录到12月3号,是否会被正确计算为连续。不过,只要日期处理正确,应该没问题。 另外,用户可能需要处理的是特定时间段的数据,比如引用1中提到的2021年12月份的数据,这时候需要在最内层的查询中加入where条件,过滤createdTime在12月份的记录。 最后,写SQL的时候要注意窗口函数的使用,确保partition by和order by正确。还有子查询的嵌套结构,可能需要多层子查询来实现。</think>### SQL查询连续N登录用户数的实现方法 #### 核心思路 通过**窗口函数**和**日期差值计算**识别连续登录行为,具体步骤如下: 1. **去重**:确保每只保留一条登录记录 2. **排序**:按用户分组、按登录日期排序生成行号 3. **差值计算**:用登录日期减去行号生成关键差值`diff_date` 4. **分组统计**:相同`user_id + diff_date`组合的数即为连续登录数[^2][^3] #### 完整SQL实现(MySQL语法示例) ```sql SELECT user_id, COUNT(*) AS consecutive_days FROM ( SELECT user_id, login_date, DATE_SUB(login_date, INTERVAL rn DAY) AS diff_date FROM ( SELECT user_id, login_date, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS rn FROM ( -- 去重步骤(假设原始表为login_info) SELECT DISTINCT user_id, DATE(createdTime) AS login_date FROM user_active_log WHERE createdTime BETWEEN '2021-12-01' AND '2021-12-31' ) t ) tt ) ttt GROUP BY user_id, diff_date HAVING consecutive_days >= 7; -- 修改数字7即可调整N的值 ``` #### 关键步骤说明 | 步骤 | 代码示例 | 作用说明 | |------|----------|----------| | 去重 | `SELECT DISTINCT user_id, DATE(createdTime)` | 处理同用户单日多次登录[^2] | | 生成行号 | `ROW_NUMBER() OVER (PARTITION BY user_id...)` | 建立连续登录的参照系[^4] | | 计算差值 | `DATE_SUB(login_date, INTERVAL rn DAY)` | 连续登录时差值恒定[^3] | | 结果筛选 | `HAVING consecutive_days >= N` | 确定满足条件的连续数 | #### 注意事项 1. **日期处理**:使用`DATE()`函数统一时间格式 2. **性能优化**:在子查询中先过滤时间范围(如WHERE createdTime...) 3. **跨平台适配**:不同数据库的日期计算函数可能不同(如PostgreSQL用`- interval`) 相关问题
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值