国内海外服务器测评及优惠
Linux服务器运维救灾服务

SQL 如何处理“连续登录天数”或“连续打卡”问题

核心思路是用ROW_NUMBER()生成行号,以“登录日期-行号”作为连续段分组依据;需确保日期为DATE类型、联合去重,并注意窗口排序方向以支持倒推连续天数。

用 ROW_NUMBER() 配合日期差计算连续段

核心思路是:对用户登录记录按日期排序,生成行号;再用登录日期减去这个行号——同一连续段内的结果必然相同。这是因为等差数列的“日期 – 行号”是定值。

常见错误是直接用 LAG() 比较前一天是否存在,逻辑易断、难以统计长度;或者用自连接暴力匹配,数据量大时性能崩盘。

  • 必须确保日期字段是 DATE 类型(不是 DATETIME),否则 DATE_SUB(login_date, INTERVAL rn DAY) 会因时间部分偏差导致分组错乱
  • 用户 ID 和日期需联合去重,重复打卡或同天多条记录必须先 GROUP BY user_id, DATE(login_time)
  • MySQL 8.0+、PostgreSQL、SQL Server 2012+ 支持 ROW_NUMBER();旧版 MySQL 只能用变量模拟,稳定性差
SELECT user_id, COUNT(*) AS days, MIN(login_date) AS start_date, MAX(login_date) AS end_date
FROM (
  SELECT user_id, login_date,
         DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) DAY) AS grp
  FROM (SELECT DISTINCT user_id, DATE(login_time) AS login_date FROM login_log) t
) t2
GROUP BY user_id, grp
HAVING COUNT(*) >= 3;

识别“当前连续登录天数”要重排窗口

如果需求是“截至今天,用户最近连续登录了多少天”,就不能简单分组聚合,得从最新日期倒推,判断是否断连。

关键点在于:窗口函数的排序方向和起始边界。用 ORDER BY login_date DESC + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 才能逐条检查是否连续。

  • 必须过滤掉未来日期(login_date ),否则 LEAD() 或倒序窗口可能引入无效数据
  • 若某用户今天没登录,但想查“如果今天登了就是第 N 天”,那得补一条虚拟今日记录再计算,不能只依赖历史
  • Oracle 中 ROW_NUMBER() 倒序后需配合 MAX(login_date) 对齐基准日,否则“连续天数”可能偏移

MySQL 5.7 怎么绕过窗口函数限制

没有 ROW_NUMBER() 就得用变量维护状态,但要注意执行顺序不可靠、并发查询可能错乱。

一站式AI应用开发和部署工具

安全写法是:子查询先排序并强制物化(加 ORDER BY ... LIMIT 9999999 或嵌套一层 FROM (SELECT ...) t),再在外层用变量累加。

  • 变量初始化必须在同一个 SELECT 内完成,比如 @rn := 0 放在 SELECT 列表最前,且不能依赖 WHERE 条件过滤后再赋值
  • 日期比较要用 TO_DAYS() 转整数,避免 DATE_ADD() 在跨月时出错;TO_DAYS(@prev_date) + 1 = TO_DAYS(login_date) 才算连续
  • 该方案无法走索引优化,10 万行以上建议升级到 MySQL 8.0 或改用应用层聚合

连续打卡场景下“节假日/周末是否计入”怎么处理

业务规则决定技术实现:如果公司要求“仅工作日打卡才算连续”,就不能只依赖原始登录表,得关联日历维表。

典型做法是预建一张 calendar_dim 表,含 dateis_workdayis_holiday 字段,JOIN 后再跑连续段逻辑。否则每次都要用复杂 CASE 判断法定假日,SQL 可读性与维护性极差。

  • 别在 SQL 里硬编码节假日(如 login_date NOT IN ('2024-01-28', '2024-02-04')),一旦漏加或年份错,连续天数全错
  • 如果“周末打卡也有效,但节假日无效”,需把 is_workday = 1 OR (WEEKDAY(login_date) 这类混合条件提前算成布尔字段,再参与连续分组
  • 日历表必须覆盖查询时间范围前后至少 30 天,防止 DATE_SUB() 计算时出现 NULL

连续问题真正的复杂点不在 SQL 写法,而在于“连续”的定义是否稳定——业务规则一变,整个分组逻辑就得重审。字段含义模糊、日期类型混用、缺失归一化清洗,比不会写窗口函数更容易导致结果错误。

赞(0) 打赏
未经允许不得转载:linuxcto运维 » SQL 如何处理“连续登录天数”或“连续打卡”问题

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫