2022-08-01 20:17:34
使用SQL计算连续登录次数的核心方法是利用窗口函数识别连续日期序列,并通过分组统计最大连续天数。 以下是详细实现步骤及优化建议:
一、基于窗口函数的完整解决方案WITH UserLogins AS ( SELECT user_id, login_date, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS rn FROM login_table),ContinuousLogin AS ( SELECT user_id, login_date, DATE_SUB(login_date, INTERVAL rn DAY) AS date_diff FROM UserLogins),GroupedContinuousLogin AS ( SELECT user_id, date_diff, COUNT(*) AS continuous_days FROM ContinuousLogin GROUP BY user_id, date_diff)SELECT user_id, MAX(continuous_days) AS max_continuous_daysFROM GroupedContinuousLoginGROUP BY user_id;关键步骤解析:UserLogins CTE
使用ROW_NUMBER()为每个用户的登录日期分配递增序号(rn),按user_id分区并按login_date排序。
作用:为后续计算连续日期提供基准序号。
ContinuousLogin CTE
计算date_diff = login_date - INTERVAL rn DAY。
原理:连续日期的date_diff值相同(如连续3天登录时,date_diff均为首日减1天)。
示例:
登录日期:2023-10-26, 2023-10-27, 2023-10-28
对应rn:1, 2, 3
date_diff:2023-10-25(统一值)
GroupedContinuousLogin CTE
按user_id和date_diff分组,统计每组的天数(COUNT(*))。
结果:每个用户的每个连续登录段及其长度。
最终查询
按user_id分组,取continuous_days的最大值(MAX),得到每个用户的最大连续登录天数。
前两日date_diff相同(2023-10-25),归为一组(连续2天)。
第三日date_diff变为2023-10-26,单独分组(连续1天)。
索引优化
在login_table的user_id和login_date列上创建复合索引。
示例:CREATE INDEX idx_user_login ON login_table(user_id, login_date);
数据类型规范
确保login_date为DATE或DATETIME类型,避免隐式类型转换。
避免全表扫描
避免在WHERE子句中对列使用函数(如WHERE DATE(login_date) = '2023-10-26'),改用范围查询:WHERE login_date BETWEEN '2023-10-26' AND '2023-10-26 23:59:59'
查询计划分析
使用数据库工具(如MySQL的EXPLAIN)分析执行计划,重点关注全表扫描、临时表使用等瓶颈。
游标/循环
缺点:性能较差,需逐行处理数据,不适合大规模数据集。
适用场景:无窗口函数支持的旧版数据库(如早期MySQL版本)。
LAG()/LEAD()函数
优势:可直接访问前一行或后一行的数据,简化连续日期判断。
示例(计算连续登录的另一种实现):WITH LaggedLogins AS ( SELECT user_id, login_date, LAG(login_date) OVER (PARTITION BY user_id ORDER BY login_date) AS prev_date FROM login_table),ContinuousSegments AS ( SELECT user_id, login_date, SUM(CASE WHEN DATEDIFF(login_date, prev_date) = 1 THEN 0 ELSE 1 END) OVER (PARTITION BY user_id ORDER BY login_date) AS group_id FROM LaggedLogins),GroupedStats AS ( SELECT user_id, group_id, COUNT(*) AS continuous_days FROM ContinuousSegments GROUP BY user_id, group_id)SELECT user_id, MAX(continuous_days) AS max_continuous_daysFROM GroupedStatsGROUP BY user_id;
原理:通过LAG()获取前一日期,判断是否连续(DATEDIFF=1),不连续时生成新分组ID(group_id),最后统计每组天数。
数据库特定函数
PostgreSQL:可使用tsrange或自定义聚合函数。
Oracle:利用CONNECT BY实现递归查询。
连续日期的数学关系(date_diff相同或日期差为1)。
分组统计时需按用户及连续标识(date_diff或group_id)分组。