需确保原始日志含user_id、event_time、event_name、device_id/os_type、channel、app_version字段;用首次启动或注册时间定义首日活跃用户;次日/7日/30日留存须以同一first_active用户集为分母,按渠道/版本下钻时直接group by对应字段即可。
☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 多模态理解力帮你轻松跨越从0到1的创作门槛☜☜☜

做留存分析时需要快速生成标准口径的次日/7日/30日留存率表格,并能按渠道、版本、设备等维度下钻对比,避免每次从零写SQL或手动计算出错。
准备原始行为日志表
确保数据表中至少包含:用户唯一ID(如user_id)、事件时间(event_time,格式为'YYYY-MM-DD HH:MM:SS'或精确到秒的时间戳)、事件类型(event_name,如'launch'表示启动)、设备信息(device_id或os_type)、渠道来源(channel)、APP版本(app_version)。
若event_time为字符串但含毫秒(如'2026-06-01 08:23:45.123'),需先截断或转为datetime类型,否则按天分组会失败。
这一步不可跳过——【缺少user_id或event_time字段将导致后续所有留存计算无意义】。
定义活跃用户与首日基准
方法一:用首次启动(first_launch)作为用户生命周期起点
执行SQL提取每个user_id的最小event_time(且event_name = 'launch'),生成首日表first_active;注意必须加WHERE event_name = 'launch'过滤,否则注册事件、静默推送等干扰事件会污染首日判定。
方法二:用注册时间(register_time)替代,适用于有明确注册闭环的产品;此时需确认register_time来自统一认证服务,且未被客户端伪造。
计算次日留存(核心逻辑)
第一步:以first_active表为左表,关联原日志表(限定event_name = 'launch'且日期 = first_active.date + INTERVAL 1 DAY)
第二步:GROUP BY first_active.date,统计COUNT(DISTINCT user_id) / COUNT(DISTINCT first_active.user_id) AS retention_d1
第三步:添加WHERE条件排除测试账号(如user_id LIKE 'test%' OR channel = 'internal'),否则线上报表会出现异常高留存。
这一步输出结果应为每日期一行,含date、retention_d1两列;若某日retention_d1 > 1.0,说明user_id去重失效或存在脏数据重复上报。
批量生成7日、30日留存
方法一(推荐):复用次日SQL结构,仅将INTERVAL 1 DAY改为INTERVAL 7 DAY和INTERVAL 30 DAY,分别运行三次并UNION ALL合并结果。
方法二:用窗口函数+日期差动态计算,但要求数据库支持LAG或DATE_DIFF(如BigQuery、Spark SQL),MySQL 8.0以下不适用。
注意:7日留存的分母必须与次日留存完全一致(即同一批first_active用户),不能用“第7天活跃用户数 ÷ 第7天新增用户数”——这种算法实际是“第7天新老用户占比”,不是留存。
按渠道/版本下钻对比
在first_active表中保留channel、app_version字段,JOIN时一并带上;计算留存率时,GROUP BY first_active.date, first_active.channel即可得到分渠道曲线。
导出后建议用Excel或QuickSight叠加折线图:横轴为first_active.date,多条线代表不同channel,Y轴为retention_d1。若某渠道曲线持续低于均值5%以上,需检查其落地页跳失率或安装包签名是否异常。
这一步不需要额外清洗——只要first_active表里channel字段非空且枚举值稳定(如只有'ios_appstore'、'android_oppo'等),直接分组即可生效。










