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.pswp.cn/diannao/92899.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92899.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于单片机的数字电压表设计

2 系统原理及基本框图 如图2.1所示,模拟电压经过档位切换到不同的分压电路衰减后,经隔离干扰送到A/D转换器进行A/D转换,然后送到单片机中进行数据处理。处理后的数据送到LCD中显示,同时通过串行通讯与上位机通信。图2.1系统基本方…

[NLP]UPF基本语法及其在 native low power verification中的典型流程

UPF基本语法及其在 native low power verification中的典型流程 摘要:本文首先简要介绍 UPF(Unified Power Format),然后解释其在 native low power verification(原生低功耗验证)中的典型流程。最后,我将使用50个具体例子来完整展示 UPF 的关键语法。这些例子基…

fish-speech 在50系列显卡使用 --compile加速兼容

#环境说明 GPU: NVIDIA GeForce RTX 5080 Laptop GPU (sm_120) win11家庭版 24H2 #问题汇总 baize.exceptions.HTTPException: (500, "RuntimeError: ptxas failed with error code 4294967295: \\n\\n") 问题汇总 1 baize.exceptions.HTTPException: (500, "…

UI自动化测试实战

Python接口自动化测试零基础入门到精通(2025最新版)一、设计背景 随着IT行业的发展,产品愈渐复杂,web端业务及流程更加繁琐,目前UI测试仅是针对单一页面,操作量大。为了满足多页面功能及流程的需求及节省工…

面试实战,问题六,被问数据库索引,怎么回答

Java开发面试:数据库索引的原理及常见问题解答 在Java开发面试中,数据库索引是核心知识点,涉及数据库优化和性能调优。索引通过高效的数据结构加速数据检索,降低磁盘IO成本,并支持排序操作。下面我将逐步解释索引的原理…

ARM-I2C硬实现

硬件I2C-GD32F4系列的实现初始化操作在初始化函数里执行以下代码uint32_t i2cx_scl_port_rcu RCU_GPIOB; uint32_t i2cx_scl_port GPIOB; uint32_t i2cx_scl_pin GPIO_PIN_6; uint32_t i2cx_scl_af GPIO_AF_4;uint32_t i2cx_sda_port_rcu RCU_GPIOB; uint32_t i2cx_sda_po…

WinUI3开发_过渡动画

简介 过渡动画是当发生事件时控件UI状态发生改变时以一种动画形式来演变到另外一种状态,而非瞬间改变,使用一种更加平滑的方式来进行切换,例如下图是文字切换的交叉栅栏效果:还有页面切换动画:在或者是图标动画&#x…

Linux下提权root权限

现在AI工具这么丰富,稍微搜一下就有一个差不多的总结输出。但是,可能还不够详细,或者给得太多~~~今天时间关系,今天只总结了在Linux如何提权到root,并没有写如何进行防护。后面有时间,我再总结一下。命令实…

焊接机器人节能先锋

汽车制造业中,机器人技术的应用已成为推动工业自动化和生产效率提升的重要力量。机器人在焊接、组装、涂装等关键制造环节中扮演着不可或缺的角色。随着工业生产规模的不断扩大,能源消耗和成本控制成为了企业必须面对的重大挑战。尤其是工业焊接用气的大…

MinIO:云原生对象存储的终极指南

MinIO 是什么? MinIO 是一款高性能、云原生的对象存储服务,具有以下优势: 轻量级部署:采用 Go 语言编写,资源占用低,支持快速部署 兼容性强:完全兼容 Amazon S3 API,轻松对接现有应用 高可用架构:支持分布式部署,确保数据持久性和服务可用性 高性能表现:专为云环境…

Spring AOP `MethodInvocation` 工作原理

⚙️ 一、通知到 MethodInterceptor 的转换机制 Spring AOP 通过适配器模式将开发者定义的注解型通知(如 Before)统一转换为 MethodInterceptor 接口实现,确保所有通知类型能接入同一调用链。以下是转换细节: 1. 适配器实现原理 核…

PPO原论文阅读

一、Introduction1.目前存在的问题:(deep)Q-learning:在一些简单问题上表现不佳,可理解性差基础的policy gradient算法:(如REINFORCE)鲁棒性差,需要大量数据TRPO:复杂,在包含噪音&am…

零基础也能创作专属歌曲:文心一言+蘑兔AI协同教程

在AI技术飞速发展的今天,音乐创作已不再是专业音乐人的专属领域。通过文心一言与蘑兔AI的协同使用,即使没有音乐基础,也能轻松完成从歌词创作到作曲编曲的全流程。本文将详细拆解操作步骤,助你快速上手,实现音乐创作梦…

图论:搜索问题

提到图论中的搜索问题,首先想到的也就是DFS和BFS了,而提到这两种搜索,那么最典型的题目就是岛屿问题了,下面就练习几道相关的题目,为之后的更深奥的图论学习打下基础! 孤岛的总面积 题目链接:…

AI驱动攻防升级,API安全走到关键档口

在数字化转型与AI技术快速发展的双重驱动下,API已成为企业业务与外部世界连接的神经中枢。然而,随着API的深度应用,针对API的攻击规模与复杂性也在持续升级。 API为何频频成为黑客重点盯防的突破口?企业常见的API防护手段是否还能…

网络基础DAY18-动态路由协议基础

动态路由协议基础知识回顾:1.什么是路由? 答:是三层设备转发IP报文的路径信息。 2.路由有哪些来源? 答:1.直连路由2.静态路由3.动态路由 3.有直连路由的条件? 答:1.二层和三层物理接口状态为UP …

axios统一封装规范管理

新建/api/ 1.新建统一处理文件/api/axios.ts import axios from "axios"const http axios.create({baseURL: import.meta.env.VITE_API_BASE_URL, // 从环境变量读取timeout: 10000, });// 请求拦截器(如添加 Token) http.interceptors.reque…

Java学习第七十四部分——Elasticsearch(ES)

目录 一、前言提要 二、核心特性 三、应用场景 四、主要优势 五、集成方式 六、基础操作 七、高级特性 八、概念类比——与关系型数据库 九、简单示例——实现存储与搜索 十、生态集成——基于Spring Data Elasticsearch 十一、性能优化建议 十二、总结归纳概述 一…

TDengine 转化函数 TO_UNIXTIMESTAMP 用户手册

TDengine TO_UNIXTIMESTAMP 函数用户使用手册 函数概述 TO_UNIXTIMESTAMP 是 TDengine 中的标量函数,用于将符合 ISO8601/RFC3339 标准的日期时间字符串转换为 Unix 时间戳。与 TO_TIMESTAMP 不同,该函数专门处理标准格式的时间字符串,无需指…

Java 中的排序算法详解

目录 一、冒泡排序(Bubble Sort) 原理​ 二、选择排序(Selection Sort) 原理​ 三、插入排序(Insertion Sort) 原理​ 四、快速排序(Quick Sort) 原理​ 五、归并排序&…