823 字
4 分钟
3月29日刷题笔记--SQL连续登录天数

本篇题目#

  • 题目一:2023年1月用户最长连续登录天数(登陆表 tb_dau
  • 题目二:找出存在连续登录 ≥ 3 天的用户(登录表 login_tb

核心原理:日期 - 行号 = 常数#

连续序列中,日期减去其行号的结果是一个固定不变的常数。

一旦序列中断,差值就会改变,从而把不同的连续段自然地区分开。

举例说明#

用户 10000 的登录数据:

fdaterkDATE_SUB(fdate, rk)
2023-01-0112022-12-31
2023-01-0222022-12-31
2023-01-0432023-01-01
  • 01-01 和 01-02 连续 → 差值相同,都是 2022-12-31,属于同一段
  • 01-04 中断了 → 差值变为 2023-01-01,另起一段

解题五步模板#

去重 → 编号 → 差值分组 → 聚合得到段长度 → 取结果
-- Step 1:去重 + 编排行号
WITH t1 AS (
SELECT DISTINCT
user_id,
fdate,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY fdate) AS rk
FROM tb_dau
WHERE fdate BETWEEN '开始日期' AND '结束日期'
),
-- Step 2:用"日期 - 行号"标记连续分组,统计每段长度
t2 AS (
SELECT
user_id,
DATE_SUB(fdate, INTERVAL rk DAY) AS diff, -- 差值相同 = 同一连续段
COUNT(1) AS consec_days
FROM t1
GROUP BY user_id, DATE_SUB(fdate, INTERVAL rk DAY)
)
-- Step 3:按需求取结果
SELECT
user_id,
MAX(consec_days) AS max_consec_days
FROM t2
GROUP BY user_id;

题目一:最长连续登录天数#

题意:统计 2023 年 1 月每个用户最长的连续登录天数。

输入tb_dau):

fdateuser_id
2023-01-0110000
2023-01-0210000
2023-01-0410000

输出

user_idmax_consec_days
100002

完整 SQL#

WITH t1 AS (
SELECT DISTINCT user_id, fdate,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY fdate) AS rk
FROM tb_dau
WHERE fdate BETWEEN '2023-01-01' AND '2023-01-31'
),
t2 AS (
SELECT user_id, COUNT(1) AS consec_days
FROM t1
GROUP BY user_id, DATE_SUB(fdate, INTERVAL rk DAY)
)
SELECT user_id, MAX(consec_days) AS max_consec_days
FROM t2
GROUP BY user_id;

执行过程拆解#

t1 的结果(去重 + 编号):

user_idfdaterk
100002023-01-011
100002023-01-022
100002023-01-043

t2 的结果(差值分组 + 计数):

user_iddiffconsec_days
100002022-12-312
100002023-01-011

最终MAX(consec_days) = 2


题目二:筛选连续登录 ≥ 3 天的用户#

题意:从登录记录中,找出在任意时间段内存在连续登录 ≥ 3 天的用户,只保留已注册用户。

完整 SQL#

WITH T1 AS (
SELECT DISTINCT user_id, DATE(log_time) AS login_time
FROM login_tb
WHERE user_id IN (SELECT user_id FROM register_tb) -- 只看已注册用户
),
T2 AS (
SELECT user_id, login_time,
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY login_time) AS rn
FROM T1
),
T3 AS (
SELECT user_id, COUNT(1) AS consec_days
FROM T2
GROUP BY user_id, DATE_SUB(login_time, INTERVAL rn DAY)
)
SELECT DISTINCT user_id
FROM T3
WHERE consec_days >= 3
ORDER BY user_id;

与题目一的区别#

题目一题目二
目标最长连续天数是多少是否存在连续 ≥ N 天
最终取法MAX(consec_days)WHERE consec_days >= 3
额外处理日期范围过滤关联注册表过滤无效用户

常见变种汇总#

题目问法Step 3 写法
最长连续天数MAX(consec_days)
是否存在连续 N 天WHERE consec_days >= N
连续 N 天的用户名单WHERE consec_days >= N + SELECT DISTINCT user_id

容易踩的坑#

① 忘记 DISTINCT

同一天登录多次会产生重复行,行号就乱了,差值不再稳定,必须先去重

② 用 RANK 代替 ROW_NUMBER

RANK 遇到并列会跳号(1, 1, 3),差值就不准了,必须用 ROW_NUMBER

③ 日期序列 vs 数字序列

-- 日期序列用 DATE_SUB
DATE_SUB(fdate, INTERVAL rk DAY)
-- 数字序列直接相减
val - rk

总结#

  • 核心公式:连续序列中,日期 - 行号 = 常数,差值相同即同一连续段
  • 五步模板:去重 → 编号 → 差值分组 → COUNT 段长 → 取结果
  • Step 3 灵活变:根据题目要求换用 MAX / WHERE / MIN+MAX
3月29日刷题笔记--SQL连续登录天数
https://www.yyylegend.com/posts/3月29日刷题笔记--sql连续登录天数/
作者
YYYLEGEND
发布于
2026-03-29
许可协议
CC BY-NC-SA 4.0