一聚教程网:一个值得你收藏的教程网站

热门教程

怎样通过SQL分组查询找出连续登录天数超过3天的用户

时间:2026-07-03 10:57:46 编辑:袖梨 来源:一聚教程网

核心思路是用ROW_NUMBER()编号后日期减编号得相同锚点日期以标识连续段;需按user_id分组排序、统一日期类型、建联合索引优化性能,并注意去重和数据库语法差异。

用 ROW_NUMBER() 和日期差构造连续登录组

核心思路是:把每个用户的登录日期按顺序编号,再用日期减去编号,同一连续段的结果会相同。比如 2024-01-01、02、03 编号为 1/2/3,相减都得 2023-12-31 —— 这个“锚点日期”就是连续段的起始基准。

实操时注意三点:

  • ROW_NUMBER() 必须按 user_id 分组、login_date 排序,否则编号错乱
  • 日期类型要统一,login_date 不能是 DATETIME 或带时分秒,否则需先 CAST(login_date AS DATE)
  • MySQL 8.0+、PostgreSQL、SQL Server 都支持 ROW_NUMBER();MySQL 5.7 及更早版本需用变量模拟,容易出错,建议升级或换方案

GROUP BY 锚点日期后筛选长度

构造出锚点列(比如叫 grp)后,对 user_idgrp 联合分组,再用 COUNT(*) 算连续天数。

典型写法:

SELECT user_idFROM (  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 login_log) tGROUP BY user_id, grpHAVING COUNT(*) > 3;

注意:HAVING COUNT(*) > 3 表示“超过3天”,即至少4天连续;若要“≥3天”,改用 >= 3

处理重复登录和跨年边界

用户同一天多次登录会导致 COUNT(*) 偏大,必须先去重:

  • 在子查询里加 DISTINCT user_id, login_date,或用 GROUP BY user_id, login_date 预聚合
  • 跨年不构成障碍,DATE_SUB 在 MySQL 中能正确处理如 2023-12-31 - INTERVAL 1 DAY = 2023-12-30,无需额外适配
  • 但若数据库用的是 Oracle 或 PostgreSQL,DATE 运算语法不同:PostgreSQL 用 login_date - ROW_NUMBER() OVER (...)::INT,Oracle 用 login_date - ROW_NUMBER() OVER (...)

性能关键:索引必须覆盖 user_id + login_date

没索引时,PARTITION BY user_id ORDER BY login_date 会触发全表扫描,百万级数据可能卡住。

建索引命令:

CREATE INDEX idx_user_login ON login_log (user_id, login_date);

这个联合索引同时支撑分组、排序和去重,比单列索引有效得多。如果表已有主键含这两列,确认顺序是否匹配——user_id 必须在前。

连续登录分析本身不难,难点在数据质量:空值、时区混杂、日志漏采都会让 grp 计算失准,上线前务必用真实样本跑一遍中间结果。

热门栏目