如何用ROW_NUMBER快速计算SQL表中连续登录天数,构造等差数列?
- 内容介绍
- 相关推荐
本文共计738个文字,预计阅读时间需要3分钟。
由于用户登录日期可能不连续、存在重复、跨月/跨年跳跃,使用`DATEDIFF()`函数计算天数差会将中间缺失的天数也计入,无法真实反映连续登录的情况。而使用`ROW_NUMBER()`按登录时间排序后生成的递增序号,与日期本身结合构成可排序序列,只要`login_date + ROW_NUMBER()`的结果相同,即可说明这些日期是连续的(即差分数列为1)。
关键点在于:同一连续段内,login_date - ROW_NUMBER() 是常量;段与段之间这个值会跳变。
构造分组标识:用 DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (...) DAY)
MySQL 8.0+ 或 PostgreSQL 中需把日期转为可运算类型。以 MySQL 为例,核心是构造一个稳定的“连续段锚点”:
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 user_login;
这个 grp 就是每个连续登录段的唯一标识。之后按 user_id 和 grp 分组,COUNT(*) 就是该段连续天数。
注意:ROW_NUMBER() 必须加 PARTITION BY user_id,否则全表编号会混掉不同用户;ORDER BY login_date 缺失会导致分组错乱。
本文共计738个文字,预计阅读时间需要3分钟。
由于用户登录日期可能不连续、存在重复、跨月/跨年跳跃,使用`DATEDIFF()`函数计算天数差会将中间缺失的天数也计入,无法真实反映连续登录的情况。而使用`ROW_NUMBER()`按登录时间排序后生成的递增序号,与日期本身结合构成可排序序列,只要`login_date + ROW_NUMBER()`的结果相同,即可说明这些日期是连续的(即差分数列为1)。
关键点在于:同一连续段内,login_date - ROW_NUMBER() 是常量;段与段之间这个值会跳变。
构造分组标识:用 DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (...) DAY)
MySQL 8.0+ 或 PostgreSQL 中需把日期转为可运算类型。以 MySQL 为例,核心是构造一个稳定的“连续段锚点”:
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 user_login;
这个 grp 就是每个连续登录段的唯一标识。之后按 user_id 和 grp 分组,COUNT(*) 就是该段连续天数。
注意:ROW_NUMBER() 必须加 PARTITION BY user_id,否则全表编号会混掉不同用户;ORDER BY login_date 缺失会导致分组错乱。

