正确统计新增用户需先计算每人首次登录日(first_login),再按该日期聚合;留存分析须用left join保留零回访样本,并用case when实现多日留存统一计算,cohort分析应以first_login为分组键且标注不完整周期。

用 GROUP BY + DATE 聚合新增用户,但必须限定首日范围
直接对 login_date 做 GROUP BY 会把所有登录都当“新增”,结果完全失真。真正要聚合的是“首次登录发生在当天”的用户数,也就是分母。关键在过滤:必须先识别每个用户的 first_login,再只取那些 first_login = '2026-06-01'(或某具体日期)的记录作为当日新增。
- 错误写法:
SELECT login_date, COUNT(DISTINCT user_id) FROM user_logins GROUP BY login_date—— 这统计的是每日活跃,不是新增 - 正确路径:先用
MIN(login_date) OVER (PARTITION BY user_id)或子查询算出每人首次登录日,再WHERE first_login BETWEEN '2026-06-01' AND '2026-06-15' - 别漏掉脏数据:加
AND login_date IS NOT NULL,否则MIN()可能返回NULL并污染分母
LEFT JOIN 构建“新增→回访”映射,避免丢失零留存样本
如果只 INNER JOIN 次日登录记录,那没回访的用户就彻底消失,分母变小、留存率虚高。必须用 LEFT JOIN 保留所有新增用户,靠 d.user_id IS NOT NULL 判断是否留存。
- MySQL 写法示例:
LEFT JOIN daily_login d ON n.user_id = d.user_id AND d.login_date = DATE_ADD(n.first_login, INTERVAL 1 DAY) - Hive/MaxCompute 替换为:
DATE_ADD(n.first_login, 1, 'dd')或TO_DATE(ADD_MONTHS(TO_DATE(n.first_login, 'yyyy-MM-dd'), 0), 'yyyy-MM-dd')(注意函数差异) - JOIN 条件里必须同时匹配
user_id和channel(如果按渠道分组),否则跨渠道误关联
用条件聚合(CASE WHEN)一次性算多日留存,别重复 JOIN
为次日、7日、30日各写一次 LEFT JOIN 不仅冗长,还容易因别名冲突或条件漏写导致结果错乱。用单次 JOIN + 多个 CASE WHEN 更稳。
- 示例:
COUNT(DISTINCT CASE WHEN d1.user_id IS NOT NULL THEN n.user_id END) AS day1_retained - 对应多个回访表时,用不同别名(
d1,d7,d30)并确保各自ON条件中的日期偏移准确 - 注意:
COUNT(DISTINCT)在大数据量下性能差,若只需比例可改用SUM(CASE WHEN ... THEN 1 ELSE 0 END)加整型除法
按 cohort(获客日期)分组后,再计算滚动留存率变化
单纯看“6月1日新增用户的次日留存是32%”意义有限;真正要发现规律,得把不同获客日期的同周期留存拉到同一横轴上对比——也就是 cohort 表。核心是把 first_login 当作 cohort_key,再算每个 cohort 的 day1_retention、day7_retention 等。
- 别用
login_date当 cohort_key,那是行为日,不是获客日 - cohort 宽表里每一行代表一个获客日,列是
day0(新增数)、day1、day7… 值是留存用户数,不是比率 - 比率必须在外部计算:因为分母是固定 cohort 的
day0,不能在 SQL 里直接除(会丢失精度或触发整除)
最易被忽略的是 cohort 边界:如果观察期截止到 6 月 15 日,那么 6 月 10 日及之后的 cohort 就无法算满 30 日留存,必须显式标注“不可用”或截断,否则拿部分数据硬算会误导结论。











