问题描述:
数据表字段:user_id,login_date,一天多次登录只算当天登录一次。
问题需求1:
统计每个用户的总登陆天数
SELECT user_id, COUNT(DISTINCT login_date) AS total_login_days FROM your_table_name -- 替换成你实际的表名 GROUP BY user_id;问题需求2:
统计每个用户的连续登陆天数
思路:
假设有个用户连续三天登录:
- 1号(序号是1):用 1号 减去 1天,结果是0号
- 2号(序号是2):用 2号 减去 2天,结果还是0号
- 3号(序号是3):用 3号 减去 3天,结果依然是0号
你看,只要是连续的,算出来的结果(也就是那个“差值”)永远都是同一个数(比如都是0号)。
那如果中间断了一天呢?比如跳到了5号:
- 5号(序号变成了4):用 5号 减去 4天,结果就变成了1号。
结果从“0号”变成了“1号”,不一样了!这就说明中间断开了,属于新的一段连续登录。
简单总结一下就是:
我们在 SQL 里算出这个“差值”,然后按这个差值分组。只要差值一样,就说明这几天是连着的;差值变了,就说明中间断过。这样解释是不是清楚多啦?😄
💡 代码逻辑拆解
- 去重 (
DISTINCT):先把同一天内的多次登录合并成一条,避免干扰后续的序号排列。 - 打标 (
ROW_NUMBER+DATE_SUB):ROW_NUMBER()会为每个用户的登录记录按时间先后生成 1, 2, 3... 的序号。- 假设某用户连续在
01-01,01-02,01-03登录,序号分别是 1, 2, 3。 - 用日期减去序号(
01-01减 1天 =12-31;01-02减 2天 =12-31...),你会发现算出来的grp都是同一天(12-31)。这个grp就是该段连续登录的“唯一锚点”。
- 分组统计 (
GROUP BY):只要grp相同,就说明它们在同一段连续登录区间里。直接按user_id和grp分组,COUNT(*)就能算出这段连续了多少天。 - 筛选 (
HAVING):最后可以根据业务需求,比如筛选出COUNT(*) >= 3的“忠实用户”。
SELECT user_id, MIN(login_date) AS start_date, -- 这段连续登录的开始日期 MAX(login_date) AS end_date, -- 这段连续登录的结束日期 COUNT(*) AS consecutive_days -- 连续登录的天数 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, login_date FROM user_login ) t1 ) t2 GROUP BY user_id, grp -- 第三步:按用户和差值日期分组 HAVING COUNT(*) >= 3; -- 第四步:筛选(比如找出连续登录3天及以上的记录)问题需求3:
统计每个用户的最长连续登陆天数
如果你不需要列出每一段连续记录,只想知道每个用户历史上最长连续登了多少天,只需要在上面的基础上再套一层MAX聚合:
SELECT user_id, MAX(consecutive_days) AS max_consecutive_days -- 取出该用户所有连续段中的最大值 FROM ( SELECT user_id, COUNT(*) AS consecutive_days FROM ( SELECT user_id, DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) DAY) AS grp FROM ( SELECT DISTINCT user_id, login_date FROM user_login ) t1 ) t2 GROUP BY user_id, grp ) t3 GROUP BY user_id;问题需求4:
想看“连续登录天数最长”的用户
SELECT user_id, MAX(consecutive_days) AS max_consecutive_days -- 算出每个用户的最长连续天数 FROM ( SELECT user_id, COUNT(*) AS consecutive_days FROM ( SELECT user_id, -- 核心:日期减去序号,得出连续分组的基准日期 DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) DAY) AS grp FROM ( SELECT DISTINCT user_id, login_date FROM user_login ) t1 ) t2 GROUP BY user_id, grp ) t3 GROUP BY user_id ORDER BY max_consecutive_days DESC -- 按最长天数降序排列 LIMIT 1; -- 只取第1名(如果想看前10名,就改成 LIMIT 10)