#2 SQL 中等

如何用 SQL 计算用户次日留存率?请说清口径与实现思路。

留存用户行为日期对齐

🎯 考察意图

留存是数据分析最高频指标之一。考察口径意识(按什么算「活跃」、分母是谁)和把业务定义落成可复现 SQL 的能力。

📐 回答框架

  1. 先问清/说清口径:活跃定义、新老用户、时区、是否去重。
  2. 明确分子分母:分母=D0 活跃(或新增)用户集合;分子=其中 D1 仍活跃。
  3. 给出实现路径:先取每日活跃用户表 → 自连接/日期偏移对齐 → 聚合。
  4. 提到常见变体:新增留存 vs 活跃留存、N 日留存、留存曲线。

💬 参考答案(口述版)

先定口径(口述时一定要先说,否则数字不可比):
- 活跃:当日有启动 / 有关键事件(需与业务对齐)
- 对象:新增用户留存,还是全体活跃用户的次日回访
- 日期:自然日还是滚动 24h;时区用业务时区

经典「新增次日留存」:
1. 找出每个用户的首活日 first_date
2. 找出用户所有活跃日 active_date
3. 对首活日 = D 的用户,看是否存在 active_date = D+1
4. 留存率 = 有 D+1 活跃的用户数 / D 日新增用户数

实现上常用:每日活跃用户去重表 dau(user_id, dt),再:

WITH first_day AS (
  SELECT user_id, MIN(dt) AS d0 FROM dau GROUP BY 1
)
SELECT
  f.d0,
  COUNT(DISTINCT f.user_id) AS new_users,
  COUNT(DISTINCT CASE WHEN d.dt = DATE_ADD(f.d0, 1) THEN f.user_id END) AS d1_retained,
  COUNT(DISTINCT CASE WHEN d.dt = DATE_ADD(f.d0, 1) THEN f.user_id END)
    * 1.0 / COUNT(DISTINCT f.user_id) AS d1_rate
FROM first_day f
LEFT JOIN dau d ON f.user_id = d.user_id
GROUP BY 1;

活跃留存则分母改为某日全体活跃用户,分子为次日仍活跃。

🔍 关键词

口径cohortDAU 去重DATE_ADD分子分母

💭 面试官可能的追问

追问 1:留存率突然下跌,你会怎么排查?

先核口径与数据质量(埋点、时区、去重);再切片(渠道/版本/新老/机型);对照发版与活动;看是否分母膨胀(拉新质量差)还是分子变少(产品体验/崩溃)。

追问 2:为什么有人用「队列留存」而不是「整体次日留存」?

队列(cohort)按获取日分组,能剥离「用户结构变化」对整体留存的干扰,更适合评估拉新质量与版本影响。

⚠️ 常见踩坑

  • 分母用注册用户、分子用活跃用户,口径不一致
  • 同一用户一天多次活跃未去重,导致虚高
  • 跨时区把「次日」算错

🧠 记忆锚点

先对齐口径,再写 SQL;留存本质是两个用户集合的比值。