第 N 天留存与滚动留存
了解经典留存、滚动留存和有界留存定义之间的区别。
第 N 天留存与滚动留存 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
为什么留存有多种定义
面试官很少只会说“计算留存”。更有针对性的追问是:是哪一种留存?根据定义不同,同一份数据会得出非常不同的数字。
您必须掌握的三种定义是:第 N 天(经典)留存、滚动(无界)留存,以及有界窗口留存。了解它们的区别,并询问业务需要哪一种,本身就是这道题要考查的能力。
第 N 天(经典)留存
第 N 天留存要回答的是:用户是否在首次操作后的恰好第 N 天处于活跃状态?第 1 天、第 7 天和第 30 天是移动应用中最经典的指标。
关键字是恰好。如果用户在第 6 天和第 8 天活跃,但第 7 天不活跃,那么按照经典定义,他不属于第 7 天留存用户。这种精确性使计数更加严格,也使曲线更加曲折。
计算天数差
第 N 天留存的核心,是计算队列起始日与每个活动日期之间的天数。在 Postgres 中,两个日期相减会直接得到整数天数。
其他 SQL 方言中的写法包括:SQL Server 使用 DATEDIFF(day, start, d),MySQL 使用 DATEDIFF(d, start)。请说明所使用的方言;这个概念(天偏移量)是相同的。
WITH cohort AS (
SELECT user_id, MIN(event_at::date) AS day0
FROM events GROUP BY user_id
),
act AS (
SELECT DISTINCT user_id, event_at::date AS day
FROM events
)
SELECT c.user_id, (a.day - c.day0) AS day_n
FROM cohort c
JOIN act a ON a.user_id = c.user_id;第 7 天留存查询
要得到第 7 天留存率,请统计天偏移量等于 7 的去重用户数,再除以队列规模。使用条件聚合,这样分子和分母都可以来自一次扫描。
= 7 这个相等条件(而不是 >= 7)是经典留存的标志。换成不等式会悄悄改变定义。
WITH dn AS (
SELECT c.user_id, (a.day - c.day0) AS day_n
FROM cohort c JOIN act a ON a.user_id = c.user_id
)
SELECT
COUNT(DISTINCT CASE WHEN day_n = 7 THEN user_id END) AS d7_retained,
COUNT(DISTINCT user_id) AS cohort_size,
ROUND(100.0 * COUNT(DISTINCT CASE WHEN day_n = 7 THEN user_id END)
/ NULLIF(COUNT(DISTINCT user_id), 0), 1) AS d7_pct
FROM dn;滚动(无界)留存
第 N 天的滚动留存提出了一个更宽松的问题:用户是否在第 N 天或之后的任意一天处于活跃状态?如果用户在第 7 天、第 20 天,或更晚的任何时间回来,都会被计为第 7 天留存用户。
这种定义会产生更平滑、更高的曲线,因此常用于衡量长期粘性。其标志性变化是:针对最大活动日,将 = N 改为 >= N。
使用 MAX 天数计算滚动留存
计算滚动留存的一种清晰方法是:先找出每位用户的最后活跃日偏移量(MAX),然后当该最大值 >= N 时,就将用户计为第 N 天的滚动留存用户。
执行 MAX 后每位用户只对应一行,因此计数很简单。这也清楚表明滚动留存具有单调性:如果用户在第 30 天仍被留存,那么在所有更小的 N 上也都会被留存。
WITH last_day AS (
SELECT c.user_id, MAX(a.day - c.day0) AS max_day_n
FROM cohort c JOIN act a ON a.user_id = c.user_id
GROUP BY c.user_id
)
SELECT
COUNT(*) AS cohort_size,
COUNT(*) FILTER (WHERE max_day_n >= 7) AS rolling_d7,
ROUND(100.0 * COUNT(*) FILTER (WHERE max_day_n >= 7)
/ NULLIF(COUNT(*), 0), 1) AS rolling_d7_pct
FROM last_day;有界窗口留存
中间方案是有界留存:用户在第 N 天附近的某个窗口内至少活跃过一次,例如将第 5 天到第 9 天作为“第 1 周”指标。它允许用户不在某个确切日期活跃,同时又比完全滚动的定义更少地进行平滑处理。
这是最符合实际业务的定义,因为真实的使用往往具有突发性。查询会在天偏移量上使用 BETWEEN。
SELECT
COUNT(DISTINCT CASE WHEN day_n BETWEEN 5 AND 9
THEN user_id END) AS week1_retained,
COUNT(DISTINCT user_id) AS cohort_size
FROM dn;同一用户的三种定义
让我们通过一个具体例子理解区别。某用户在第 0 天开始,此后只在第 9 天活跃过。
- 经典第 7 天:不属于留存用户(没有在恰好第 7 天活动)。
- 滚动第 7 天:属于留存用户(最大天数为第 9 天,>= 7)。
- 有界第 5–9 天第 1 周:属于留存用户(第 9 天落在该窗口内)。
同一份数据,三种答案。在面试中,请像这样讲述一个例子,以证明您理解的是语义,而不只是语法。
周期粒度:日、周与月
“第 N 天”可以泛化为第 N 个周期。对于按月使用的面向企业的产品,按天计算的留存会充满噪声;您应按月分组,并询问第 N 个月的留存。其机制完全相同,只有截断粒度发生变化。
请选择与产品自然使用节奏相匹配的粒度,并明确说明这一点。面向消费者的移动应用适合按天,使用节奏较慢的企业工具则适合按周或按月。
-- weekly grain: offset in whole weeks
SELECT
c.user_id,
FLOOR((a.day - c.day0) / 7) AS week_n
FROM cohort c
JOIN act a ON a.user_id = c.user_id;生存者偏差与成熟度陷阱
一个细致的高级面试考点是:不要报告一个只有 10 天历史的队列的第 30 天留存。它还没有获得在第 30 天活跃的机会,因此该值人为地变成了 0,并不代表真实的低留存。
报告第 N 天留存时,只纳入存续时间 >= N 的队列。请根据 CURRENT_DATE - day0 >= N 进行筛选。忘记这一点会让近期队列看起来糟糕得不真实。
WITH cohort AS (
SELECT user_id, MIN(event_at::date) AS day0
FROM events GROUP BY user_id
)
SELECT *
FROM cohort
WHERE (CURRENT_DATE - day0) >= 30; -- mature enough for Day-30明确说出您选择的定义
面试中最好的回答不是直接写查询,而是反问一句:“您需要经典的第 N 天留存、滚动留存,还是有界窗口留存?自然的周期是什么?”
然后说明取舍:经典留存严格,适合衡量确切日期的产品触发点;滚动留存会高估短期留存,但能捕捉长期粘性;有界留存则是更符合实际的折中方案。证明您是在有意识地选择指标,这正是本课的核心。
快速检查
某用户的首次操作发生在第 0 天;此后唯一一次活动发生在第 12 天。按照每种定义,在第 7 天时该用户是否属于留存用户?
回顾:留存定义
第 N 天留存与滚动留存的要点:
- 经典第 N 天留存:恰好在第 N 天活跃(偏移量
= N)— 严格,曲线曲折。 - 滚动留存:在第 N 天或之后活跃(
MAX offset >= N)— 更平滑、单调,用于衡量粘性。 - 有界留存:在某个窗口内活跃(
BETWEEN)— 符合实际的折中方案。 - 将按天的定义泛化为与产品使用节奏相匹配的任意周期粒度。
- 只有在报告第 N 天留存时纳入成熟队列(存续时间 >= N),才能避免生存者偏差陷阱,并且始终询问业务所说的究竟是哪种定义。
常见问题解答
「第 N 天留存与滚动留存」课时是免费的吗?
是的 — 「第 N 天留存与滚动留存」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「第 N 天留存与滚动留存」这节课中我会学到什么?
了解经典留存、滚动留存和有界留存定义之间的区别。 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「第 N 天留存与滚动留存」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 按首次操作定义用户群组
- 构建留存矩阵
- 第 N 天留存与滚动留存
- 流失与回归查询