当前位置: 首页 > news >正文

SQL164 2021年11月每天新用户的次日留存率

SQL164 2021年11月每天新用户的次日留存率

思路

  1. 找出新用户​:确定每个用户首次活跃的日期(即新用户)

    • 例如101用户在11月1日首次出现
  2. 处理跨天活跃​:考虑用户可能跨天活跃的情况(in_time和out_time不在同一天)

    • 例如用户可能在11月1日23:50进入,11月2日00:10离开,则算作两天都活跃
  3. 计算次日留存​:

    • 对每个新用户,检查他们首次活跃后的第二天是否仍然活跃
    • 使用LEAD窗口函数高效获取用户下一次活跃日期
  4. 计算留存率​:

    • 每天的新用户数作为分母
    • 第二天仍然活跃的新用户数作为分子
    • 两者相除得到留存率,保留2位小数

最终输出2021年11月每天新用户的次日留存率,按日期排序。

代码

WITH 
-- 获取每个用户的最早活跃日期作为其首次出现日期
first_occurrence AS (SELECT uid,DATE(MIN(in_time)) AS first_dtFROM tb_user_logGROUP BY uid
),-- 获取每个用户每天活跃的记录(处理跨天情况)
user_active_dates AS (SELECT DISTINCT uid,DATE(in_time) AS active_dateFROM tb_user_logUNIONSELECT DISTINCT uid,DATE(out_time) AS active_dateFROM tb_user_log
),-- 为每个用户按日期排序,并使用LEAD获取下一天的活跃状态
user_activity_sequence AS (SELECT uid,active_date,LEAD(active_date) OVER (PARTITION BY uid ORDER BY active_date) AS next_active_dateFROM user_active_dates
),-- 计算每天的新用户数及其次日留存情况
daily_stats AS (SELECT fo.first_dt AS dt,COUNT(DISTINCT fo.uid) AS new_users,COUNT(DISTINCT CASE WHEN DATEDIFF(uas.next_active_date, fo.first_dt) = 1 THEN fo.uid END) AS retained_usersFROM first_occurrence foLEFT JOIN user_activity_sequence uas ON fo.uid = uas.uid AND fo.first_dt = uas.active_dateWHERE fo.first_dt BETWEEN '2021-11-01' AND '2021-11-30'GROUP BY fo.first_dt
)-- 计算并格式化留存率
SELECT dt,ROUND(IF(new_users = 0, 0, retained_users / new_users), 2) AS uv_left_rate
FROM daily_stats
ORDER BY dt;

逐步展示如何计算2021年11月每天新用户的次日留存率

原始数据表 tb_user_log

iduidartical_idin_timeout_timesign_in
110102021-11-01 10:00:002021-11-01 10:00:421
210290012021-11-01 10:00:002021-11-01 10:00:090
310390012021-11-01 10:00:012021-11-01 10:01:500
410190022021-11-02 10:00:092021-11-02 10:00:280
510390022021-11-02 10:00:512021-11-02 10:00:590
610490012021-11-02 10:00:282021-11-02 10:00:500
710190032021-11-03 11:00:552021-11-03 11:01:240
810490032021-11-03 11:00:452021-11-03 11:00:550
910590032021-11-03 11:00:532021-11-03 11:00:590
1010190022021-11-04 11:00:552021-11-04 11:00:590

步骤1:确定每个用户的首次活跃日期

SELECT uid,DATE(MIN(in_time)) AS first_dt
FROM tb_user_log
GROUP BY uid;

结果:

uidfirst_dt
1012021-11-01
1022021-11-01
1032021-11-01
1042021-11-02
1052021-11-03

步骤2:处理跨天情况,获取用户活跃日期

SELECT DISTINCT uid,DATE(in_time) AS active_date
FROM tb_user_log
UNION
SELECT DISTINCT uid,DATE(out_time) AS active_date
FROM tb_user_log;

结果:

uidactive_date
1012021-11-01
1012021-11-02
1012021-11-03
1012021-11-04
1022021-11-01
1032021-11-01
1032021-11-02
1042021-11-02
1042021-11-03
1052021-11-03

步骤3:使用LEAD函数获取用户的下一次活跃日期

SELECT uid,active_date,LEAD(active_date) OVER (PARTITION BY uid ORDER BY active_date) AS next_active_date
FROM user_active_dates;

结果:

uidactive_datenext_active_date
1012021-11-012021-11-02
1012021-11-022021-11-03
1012021-11-032021-11-04
1012021-11-04NULL
1022021-11-01NULL
1032021-11-012021-11-02
1032021-11-02NULL
1042021-11-022021-11-03
1042021-11-03NULL
1052021-11-03NULL

步骤4:计算每天的新用户次日留存情况

SELECT fo.first_dt AS dt,COUNT(DISTINCT fo.uid) AS new_users,COUNT(DISTINCT CASE WHEN DATEDIFF(uas.next_active_date, fo.first_dt) = 1 THEN fo.uid END) AS retained_users
FROM first_occurrence fo
LEFT JOIN user_activity_sequence uas ON fo.uid = uas.uid AND fo.first_dt = uas.active_date
WHERE fo.first_dt BETWEEN '2021-11-01' AND '2021-11-30'
GROUP BY fo.first_dt;

结果:

dtnew_usersretained_users
2021-11-0132
2021-11-0211
2021-11-0310

详细解释一下

这个CTE是计算每日新用户及其次日留存情况的核心部分,详细拆解逻辑:

  1. 数据来源​:

    • first_occurrence:包含每个用户的首次活跃日期
    • user_activity_sequence:包含用户每次活跃日期及下一次活跃日期(使用LEAD计算)
  2. 连接条件​:

    LEFT JOIN user_activity_sequence uas 
    ON fo.uid = uas.uid 
    AND fo.first_dt = uas.active_date
    • 按用户ID连接
    • 只连接用户首次活跃当天的记录(因为我们要计算的是新用户的次日留存)
  3. 计算字段​:

    • new_users:每天首次出现的用户数(COUNT DISTINCT)
    • retained_users:这些新用户中第二天仍然活跃的数量
  4. 留存判断逻辑​:

    CASE WHEN DATEDIFF(uas.next_active_date, fo.first_dt) = 1 THEN fo.uid 
    END
    • 计算用户首次活跃日期与下一次活跃日期的差值
    • 如果差值为1天,则表示用户次日活跃
  5. 为什么用LEFT JOIN​:

    • 确保即使新用户第二天不活跃,也会被计入分母(新用户数)
    • 不活跃的用户在CASE WHEN中会返回NULL,不会被COUNT计算

示例数据推演

以2021-11-01为例:

  • 新用户:101、102、103
  • 检查他们的次日活跃情况:
    • 101:11-02活跃(符合)
    • 102:11-02不活跃
    • 103:11-02活跃(符合)
  • 结果:3个新用户,2个次日活跃 → 留存率2/3=0.67

这种设计确保了:

  1. 准确识别新用户
  2. 正确处理跨天活跃情况
  3. 精确计算次日留存率

最终结果:计算留存率

SELECT dt,ROUND(IF(new_users = 0, 0, retained_users / new_users), 2) AS uv_left_rate
FROM daily_stats
ORDER BY dt;

最终输出:

dtuv_left_rate
2021-11-010.67
2021-11-021.00
2021-11-030.00

http://www.dtcms.com/a/297949.html

相关文章:

  • ReAct Agent(LangGraph实现)
  • 去除视频字幕 2, 使用 PaddleOCR 选取图片中的字幕区域, 根据像素大小 + 形状轮廓
  • MCP 与传统集成方案深度对决:REST API、GraphQL、gRPC 全方位技术解析
  • react 内置hooks 详细使用场景,使用案例
  • 轮盘赌算法
  • Python爬虫实战:研究Talon相关技术构建电商爬虫系统
  • ZLMediaKit 源代码入门
  • Java排序算法之<选择排序>
  • IT领域需要“落霞归雁”思维框架的好处
  • 熵与交叉熵:从信息论到机器学习的「不确定性」密码
  • Jmeter的元件使用介绍:(四)前置处理器详解
  • 告别静态文档!Oracle交互式技术架构图让数据库学习“活“起来
  • 多步相移小记
  • epoll_event数据结构及使用案例详解
  • springboot(3.4.8)整合mybatis
  • 分布式方案 一 分布式锁的四大实现方式
  • android app适配Android 15可以在Android studio自带的模拟器上进行吗,还是说必须在真机上进行
  • HashMap底层实现原理与核心设计解析
  • AI同传领域,字节跳动与科大讯飞激战进行时
  • 【Linux系统】基础IO(下)
  • 深度学习篇---图像数据采集
  • classgraph:Java轻量级类和包扫描器
  • 深度学习篇---深度学习中的卡尔曼滤波
  • Vmware VSAN主机停机维护流程
  • RAG、Function Call、MCP技术笔记
  • Java中给List<String>去重的4种方式
  • 数据结构:对角矩阵(Diagonal Matrix)
  • Android UI 组件系列(八):ListView 基础用法与适配器详解
  • python语法笔记
  • 《剑指offer》-数据结构篇-链表