如何利用SQL窗口函数进行连续登录统计_巧用LAG与LEAD函数
连续登录指用户在自然日维度上登录日期连成一片需先按user_id和日期去重再用LAG()结合login_date与ROW_NUMBER()差值构造分组ID来识别连续段。连续登录怎么定义先搞清业务逻辑再写SQL连续登录不是“每天都有记录”就行而是指用户在自然日维度上登录日期连成一片。比如 2024-01-01、01-02、01-03 是连续 3 天但中间缺一天比如没有 01-02哪怕前后都有记录也不算连续。很多同学直接用 ROW_NUMBER() 做差值法结果发现漏掉跨月、跨年、或数据本身有脏值如重复登录、未来时间的情况。连续性判断必须基于去重后的 login_date不是 login_time用户可能一天多次登录得先 GROUP BY user_id, DATE(login_time) 或用 CAST(login_time AS DATE)如果原始表里 login_time 是字符串不转成日期类型就直接 LAG()会导致隐式转换失败或排序错乱LAG 函数怎么用才不出错重点看 ORDER BY 和 NULL 处理LAG() 的核心是“往前看一行”但它不会自动按日期排好队——你得明确告诉它按什么排序、分谁的组。常见错误是忘了 PARTITION BY user_id导致张三的记录和李四的混在一起比。必须写全窗口定义LAG(login_date) OVER (PARTITION BY user_id ORDER BY login_date)如果某用户只有 1 条记录LAG() 返回 NULL不能直接和当前日期做减法否则整行变 NULLMySQL 8.0 和 PostgreSQL 支持 LAG(..., 1, 1970-01-01) 设默认值但 SQLite 不支持第三个参数得用 COALESCE(LAG(...), 1970-01-01)示例片段PostgreSQLSELECT user_id, login_date, COALESCE(LAG(login_date) OVER (PARTITION BY user_id ORDER BY login_date), login_date - INTERVAL 1 day) AS prev_dateFROM (SELECT DISTINCT user_id, CAST(login_time AS DATE) AS login_date FROM login_log) t;怎么标出“连续段”的起始点用日期差造分组 ID只靠 LAG() 只能知道“前一天有没有”但没法把一串连续日期打包成一个组。这时候要构造一个不会随日期漂移的标识用 login_date - ROW_NUMBER() OVER (...)。每个连续段内login_date 递增ROW_NUMBER() 也递增差值恒定一旦断开ROW_NUMBER() 从 1 重新计差值就变了注意不同数据库对日期减整数的支持不同。PostgreSQL 可以 login_date - ROW_NUMBER()MySQL 要用 DATE_SUB(login_date, INTERVAL ROW_NUMBER() DAY)否则报错关键点 RedClaw 百度推出的手机端万能AI Agent助手