group by date 不行,因无法识别用户连续活跃区间;需用 row_number() 与日期运算构造分组标识,再按 user_id 和基准日分组统计连续段。

为什么 GROUP BY date 不行?
直接 GROUP BY date 只能统计每天的活跃用户数,但“连续日期内活跃”要求把跨天但连贯的用户行为合并成一个会话。比如用户在 2024-01-01、01-02、01-03 都登录,这算 1 个连续活跃段,而不是 3 条独立记录。
核心难点在于:SQL 没有原生“连续区间识别”能力,必须靠时间差 + 窗口函数构造分组标识。
用 ROW_NUMBER() 构造连续性标记
关键思路是:对每个用户的登录日期排序,再用日期本身减去序号——同一连续段的结果会恒定。
假设表 user_login 有字段 user_id、login_date(DATE 类型),实操步骤如下:
- 先按
user_id分组,按login_date排序,生成递增序号:ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) - 用
login_date - INTERVAL ROW_NUMBER() DAY(MySQL)或login_date - ROW_NUMBER() OVER (...) * INTERVAL '1 day'(PostgreSQL)得到“基准日” - 同一连续段的用户,其
login_date - 序号结果相同,可作为分组键
示例片段(PostgreSQL):
SELECT user_id, MIN(login_date) AS start_date, MAX(login_date) AS end_date
FROM (
SELECT user_id, login_date,
login_date - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) * INTERVAL '1 day' AS grp
FROM user_login
) t
GROUP BY user_id, grp;
统计“连续 N 天活跃”的用户总数
如果目标是“至少连续 3 天活跃的用户总数”,不能只查出区间再人工数——要嵌套聚合。
引导 OpenClaw 代理使用 exec 和 process 工具执行 Ralph Wiggum 循环。通过 pty:true 提供正确的 TTY 支持,编排编码代理(Codex, Claude Code, OpenCode, Goose)。利用 PROMPT.md、AGENTS.md、SPECS 和 IMPLEMENTATION_PLAN.md 规划和构建代码。包含规划与构建模式、背压机制、沙箱及完成条件。用户请求循环,代理使用工具执行。
常见错误是先 GROUP BY user_id, grp 算出每个连续段天数,再在外层 COUNT(DISTINCT user_id),但要注意:
- 同一个用户可能有多个 ≥3 天的连续段(如 1月1–3日、1月10–15日),是否去重取决于业务定义
- MySQL 8.0+ / PostgreSQL 支持 CTE,推荐用两层嵌套:第一层算段长,第二层过滤并计数
- 日期必须为 DATE 类型;若含时间戳,先
CAST(login_time AS DATE)再处理,否则同一天多次登录会被拆成多行
简写逻辑(PostgreSQL):
WITH segments AS (
SELECT user_id,
COUNT(*) AS duration
FROM (
SELECT user_id, login_date,
login_date - ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) * INTERVAL '1 day' AS grp
FROM user_login
) t
GROUP BY user_id, grp
)
SELECT COUNT(DISTINCT user_id)
FROM segments
WHERE duration >= 3;
不同数据库的语法坑点
ROW_NUMBER() 全都支持,但日期运算差异大:
- MySQL:用
DATE_SUB(login_date, INTERVAL ROW_NUMBER() DAY),注意ROW_NUMBER()不能直接参与表达式,需先放入子查询或 CTE - PostgreSQL:支持直接运算,但要用
INTERVAL '1 day',不是INTERVAL 1 DAY - SQLite:不支持窗口函数(3.25+ 才支持
ROW_NUMBER()),且无原生 INTERVAL,得用julianday()手动算差值 - Oracle:用
login_date - ROW_NUMBER() OVER (...)即可,日期减数字即减天数
另外,login_date 字段若有 NULL 或重复值,会导致连续性判断错乱——务必在子查询里加 WHERE login_date IS NOT NULL 并去重。
真正麻烦的不是写法,而是确认“连续”的定义:是否允许同一天多次登录只算 1 天?跨午夜的 session 是否算中断?这些得和产品对齐,代码只是执行工具。










