利用ROW_NUMBER()和日期算术将连续登录归组,通过去重处理多记录、用group_id识别断点解决不连续问题,高效判断N天连续登录。

要判断SQL中特定长度(N天)的连续登录,核心思路是利用日期算术和窗口函数来识别用户登录日期的连续性,然后计算这些连续序列的长度。通常,这涉及到为每个用户的每次登录分配一个基于日期的序列号,并通过一个巧妙的计算将连续的日期归为同一组,最后统计每组的登录天数。
判断N天连续登录,我个人最推荐且认为最通用、效率也相对较高的方法是结合
ROW_NUMBER()
我们先假设有一个
user_logins
user_id
login_date
login_date
准备数据: 首先,我们需要确保每个用户每天的登录只计算一次。如果你的
login_date
-- 示例表结构
CREATE TABLE user_logins (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
login_time DATETIME
);
-- 插入一些测试数据
INSERT INTO user_logins (user_id, login_time) VALUES
(1, '2023-01-01 10:00:00'),
(1, '2023-01-02 11:00:00'),
(1, '2023-01-03 12:00:00'), -- 连续3天
(1, '2023-01-05 09:00:00'),
(1, '2023-01-06 10:00:00'), -- 连续2天
(2, '2023-01-01 08:00:00'),
(2, '2023-01-02 09:00:00'),
(2, '2023-01-03 10:00:00'),
(2, '2023-01-04 11:00:00'), -- 连续4天
(3, '2023-01-01 07:00:00'),
(3, '2023-01-01 07:30:00'); -- 同一天重复登录核心SQL逻辑: 这个方法分几步走,用CTE(Common Table Expressions)会使代码更清晰。
WITH UserDailyLogins AS (
-- 步骤1: 提取每个用户每天的唯一登录日期
SELECT DISTINCT
user_id,
CAST(login_time AS DATE) AS login_date -- 确保只取日期部分
FROM
user_logins
),
RankedLogins AS (
-- 步骤2: 为每个用户的登录日期按顺序分配一个行号
SELECT
user_id,
login_date,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) AS rn
FROM
UserDailyLogins
),
ConsecutiveGroups AS (
-- 步骤3: 计算一个“分组ID”。
-- 如果日期是连续的,login_date - rn 的结果会保持不变。
-- 例如:
-- 2023-01-01 (rn=1) -> 2023-01-01 - 1天 = 2022-12-31
-- 2023-01-02 (rn=2) -> 2023-01-02 - 2天 = 2022-12-31
-- 2023-01-03 (rn=3) -> 2023-01-03 - 3天 = 2022-12-31 (同一组)
-- 2023-01-05 (rn=4) -> 2023-01-05 - 4天 = 2023-01-01 (新组)
SELECT
user_id,
login_date,
-- MySQL: DATE_SUB(login_date, INTERVAL rn DAY)
-- PostgreSQL: login_date - (rn * INTERVAL '1 day')
-- SQL Server: DATEADD(day, -rn, login_date)
DATE_SUB(login_date, INTERVAL rn DAY) AS group_id
FROM
RankedLogins
)
-- 步骤4: 根据 user_id 和 group_id 分组,统计连续天数,并筛选出满足N天条件的记录
SELECT
user_id,
MIN(login_date) AS consecutive_start_date,
MAX(login_date) AS consecutive_end_date,
COUNT(login_date) AS consecutive_days_count
FROM
ConsecutiveGroups
GROUP BY
user_id,
group_id
HAVING
COUNT(login_date) >= 3; -- 将这里的 '3' 替换为你想要的连续天数 N这个SQL会返回每个用户达到N天或更长连续登录的起始日期、结束日期以及实际的连续天数。这个方法非常灵活,N可以任意指定。
在实际的数据里,同一用户一天内可能有很多条登录记录,或者登录日期本身就存在跳跃,不是严格连续的。这些情况确实是判断连续登录时需要重点考虑的。
对于同一用户多条登录记录的问题,我在上面的解决方案中已经通过
UserDailyLogins
SELECT DISTINCT user_id, CAST(login_time AS DATE) AS login_date
login_time
ROW_NUMBER()
ROW_NUMBER()
group_id
至于日期不连续(存在跳跃)的问题,这正是
ROW_NUMBER()
login_date - rn
group_id
举个例子: 假设用户A的登录日期是:2023-01-01, 2023-01-02, 2023-01-05。
| user_id | login_date | rn | login_date - rn (假定日期是数字) | group_id (实际日期) |
|---|---|---|---|---|
| A | 2023-01-01 | 1 | 2023-01-01 - 1 = 2022-12-31 | 2022-12-31 |
| A | 2023-01-02 | 2 | 2023-01-02 - 2 = 2022-12-31 | 2022-12-31 |
| A | 2023-01-05 | 3 | 2023-01-05 - 3 = 2023-01-02 | 2023-01-02 |
可以看到,前两行
group_id
login_date
2023-01-05
rn
3
login_date - rn
group_id
确实,SQL的世界里解决问题的方法总是多种多样,
ROW_NUMBER()
LAG()
LEAD()
LAG()
LEAD()
LAG(login_date, 1) OVER (PARTITION BY user_id ORDER BY login_date)
当前登录日期 = 上次登录日期 + 1天
WITH UserDailyLogins AS (
SELECT DISTINCT
user_id,
CAST(login_time AS DATE) AS login_date
FROM
user_logins
),
LaggedLogins AS (
SELECT
user_id,
login_date,
LAG(login_date, 1) OVER (PARTITION BY user_id ORDER BY login_date) AS prev_login_date
FROM
UserDailyLogins
)
SELECT
user_id,
login_date,
CASE
WHEN prev_login_date IS NULL THEN 1 -- 第一个登录日
WHEN DATEDIFF(login_date, prev_login_date) = 1 THEN 1 -- 连续
ELSE 0 -- 不连续
END AS is_consecutive_from_prev
FROM
LaggedLogins;这个方法可以帮你判断每一天是否是紧接着前一天的登录。但要直接统计N天连续登录,你需要在此基础上再做一层聚合,比如用一个递归CTE或者更复杂的窗口函数组合来“串联”这些
is_consecutive
ROW_NUMBER()
group_id
LAG()
递归CTE (Recursive CTEs): 这是一种更高级的SQL技巧,可以用于处理分层数据或迭代计算。你可以定义一个锚点成员(通常是每个用户第一次登录的日期),然后通过递归成员不断地检查下一天是否登录,并累加连续天数。
-- 这是一个概念性的示例,具体实现会因数据库而异,且通常比ROW_NUMBER更复杂和耗资源
WITH RECURSIVE ConsecutiveLoginCounter AS (
-- 锚点成员:每个用户的第一天登录,或者不连续序列的起点
SELECT
user_id,
login_date,
1 AS streak_length
FROM
(SELECT DISTINCT user_id, CAST(login_time AS DATE) AS login_date FROM user_logins) AS d
WHERE NOT EXISTS (
SELECT 1 FROM (SELECT DISTINCT user_id, CAST(login_time AS DATE) AS login_date FROM user_logins) AS prev_d
WHERE prev_d.user_id = d.user_id AND prev_d.login_date = DATE_SUB(d.login_date, INTERVAL 1 DAY)
)
UNION ALL
-- 递归成员:如果下一天连续,则增加streak_length
SELECT
clc.user_id,
d.login_date,
clc.streak_length + 1
FROM
ConsecutiveLoginCounter clc
JOIN
(SELECT DISTINCT user_id, CAST(login_time AS DATE) AS login_date FROM user_logins) AS d
ON
d.user_id = clc.user_id AND d.login_date = DATE_ADD(clc.login_date, INTERVAL 1 DAY)
)
SELECT
user_id,
MAX(streak_length) AS max_consecutive_days
FROM
ConsecutiveLoginCounter
WHERE
streak_length >= N -- 筛选出满足N天条件的
GROUP BY user_id;递归CTE非常强大,
以上就是怎么用SQL判断特定长度连续登录_SQL判断N天连续登录方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号