SQL次日留存率计算精讲:自连接与多字段去重的深度应用
一、问题拆解:理解次日留存率的计算逻辑
1.1 业务需求转换
题目:运营希望查看用户在某天刷题后第二天还会再来刷题的留存率。
关键分析点:
- 留存率 = (第一天刷题且第二天再次刷题的用户数) / 第一天刷题的总用户数
- 需要关联同一用户的连续两天行为
- 结果要求不去重(保留所有可能的留存行为)
1.2 数据模型假设
假设我们有用户刷题记录表question_practice_detail
,包含:
device_id
:用户设备ID(唯一标识用户)date
:刷题日期- 其他字段:题目ID、答题结果等(与本次计算无关)
二、核心SQL解析:自连接实现留存关联
2.1 完整SQL语句
SELECT COUNT(DISTINCT q2.device_id, q2.date) / COUNT(DISTINCT q1.device_id, q1.date) AS avg_ret
FROM question_practice_detail AS q1
LEFT JOIN question_practice_detail AS q2
ON q1.device_id = q2.device_id AND DATEDIFF(q2.date, q1.date) = 1;
2.2 自连接设计原理
表别名技术:
q1
:作为主表,表示"第一天刷题记录"q2
:作为关联表,表示"第二天刷题记录"
连接条件解析:
q1.device_id = q2.device_id
:确保关联同一用户的记录DATEDIFF(q2.date, q1.date) = 1
:确保q2
的日期比q1
晚一天
左连接的意义:
- 即使某用户在次日没有刷题记录(
q2
为NULL),q1
的记录仍会被保留 - 这保证了分母(所有第一天刷题用户)的完整性
三、COUNT(DISTINCT …) 多字段去重详解
3.1 多字段去重的内在逻辑
COUNT(DISTINCT q2.device_id, q2.date)
执行步骤:
- 组合键生成:将
device_id
和date
组合成复合键(如1001-2023-01-02
) - 哈希去重:数据库内部使用哈希表对组合键进行去重
- 计数统计:统计去重后的组合键数量
与单字段去重的区别:
表达式 | 统计逻辑 |
---|---|
COUNT(DISTINCT device_id) | 统计不同用户的数量 |
COUNT(DISTINCT date) | 统计不同日期的数量 |
COUNT(DISTINCT device_id, date) | 统计不同用户+日期的组合数量 |
3.2 分子与分母的统计逻辑
分子:COUNT(DISTINCT q2.device_id, q2.date)
- 统计有次日刷题记录的
(用户ID, 日期)
组合数 - 确保每个用户每天只被统计一次
分母:COUNT(DISTINCT q1.device_id, q1.date)
- 统计所有第一天刷题的
(用户ID, 日期)
组合数 - 覆盖所有可能产生留存的基础用户
四、执行流程与数据流转
4.1 示例数据与连接过程
假设我们有以下数据:
q1表(第一天刷题记录)
device_id | date |
---|---|
1001 | 2023-01-01 |
1002 | 2023-01-01 |
1003 | 2023-01-01 |
q2表(第二天刷题记录)
device_id | date |
---|---|
1001 | 2023-01-02 |
1001 | 2023-01-02 |
自连接结果
q1.device_id | q1.date | q2.device_id | q2.date |
---|---|---|---|
1001 | 2023-01-01 | 1001 | 2023-01-02 |
1002 | 2023-01-01 | NULL | NULL |
1003 | 2023-01-01 | NULL | NULL |
4.2 统计过程详解
-
分子计算:
COUNT(DISTINCT q2.device_id, q2.date)
= 1- 去重后只有
(1001, 2023-01-02)
这一个有效组合
-
分母计算:
COUNT(DISTINCT q1.device_id, q1.date)
= 3- 包含
(1001, 2023-01-01)
、(1002, 2023-01-01)
、(1003, 2023-01-01)
-
结果:
- 次日留存率 = 1/3 ≈ 33.33%
五、性能优化策略
5.1 复合索引设计
-- 创建覆盖索引,同时加速连接和去重
CREATE INDEX idx_device_date ON question_practice_detail(device_id, date);
索引优化原理:
- 支持
device_id
的等值查询 - 支持
date
的范围查询(DATEDIFF本质是日期比较) - 覆盖索引避免回表,直接在索引中完成统计
5.2 执行计划分析
使用EXPLAIN
关键字分析SQL执行计划:
EXPLAIN
SELECT ... (原SQL) ...;
关键指标解读:
type
列:理想情况为ref
或range
,避免ALL
(全表扫描)key
列:应显示使用了idx_device_date
索引Extra
列:避免出现Using temporary
和Using filesort
六、常见问题与解决方案
6.1 NULL值处理
-- 假设存在device_id=NULL的记录
COUNT(DISTINCT device_id, date) -- 会忽略这些记录-- 如需包含NULL,需手动转换
COUNT(DISTINCT COALESCE(device_id, 0), date)
6.2 分母为零处理
当某天没有用户刷题时,直接计算会导致除零错误:
SELECT IFNULL(COUNT(DISTINCT q2.device_id, q2.date) / NULLIF(COUNT(DISTINCT q1.device_id, q1.date), 0), 0) AS avg_ret
FROM ...
6.3 时间窗口扩展
计算3日留存率:
SELECT COUNT(DISTINCT q3.device_id) / COUNT(DISTINCT q1.device_id) AS retention_3day
FROM question_practice_detail AS q1
LEFT JOIN question_practice_detail AS q3
ON q1.device_id = q3.device_id AND DATEDIFF(q3.date, q1.date) = 3;
七、总结与技术要点
7.1 核心技术点回顾
- 自连接技术:通过表别名实现同一表的不同时间关联
- COUNT(DISTINCT):多字段组合去重统计的关键
- LEFT JOIN:确保分母统计的完整性,包含所有可能留存的用户
- 索引优化:复合索引显著提升大数据量下的查询性能
7.2 技术决策树
开始
│
├── 是否需要统计用户行为留存率?
│ │
│ └── 是 → 是否需要多日留存?
│ │
│ ├── 是 → 使用DATEDIFF调整时间窗口
│ │
│ └── 否 → 是否需要去重?
│ │
│ ├── 是 → 使用COUNT(DISTINCT ...)
│ │
│ └── 否 → 直接使用COUNT
│
├── 是否存在性能问题?
│ │
│ └── 是 → 创建复合索引(用户ID, 日期)
│
└── 结束
通过深入理解自连接和多字段去重的原理,结合索引优化技术,我们可以高效、准确地计算各种时间窗口的用户留存率。
相关文章:
SQL次日留存率计算精讲:自连接与多字段去重的深度应用
一、问题拆解:理解次日留存率的计算逻辑 1.1 业务需求转换 题目:运营希望查看用户在某天刷题后第二天还会再来刷题的留存率。 关键分析点: 留存率 (第一天刷题且第二天再次刷题的用户数) / 第一天刷题的总用户数需…...

使用SQLite Studio导出/导入SQL修复损坏的数据库
使用SQLite Studio导出/导入SQL修复损坏的数据库 使用Zotero时遇到了数据库损坏,在软件中寸步难行,遂尝试修复数据库。 一、SQLite Studio简介 SQLite Studio是一款专为SQLite数据库设计的免费开源工具,支持Windows/macOS/Linux。相较于其…...
LSTM-Attention混合模型:美债危机与黄金对冲效率研究
摘要:本文依托多维度量化分析框架,结合自然语言处理(NLP)技术对地缘文本的情绪挖掘,构建包含宏观因子、风险溢价因子及技术面因子的三阶定价模型,对当前黄金市场的波动特征进行归因分析。实证结果显示&…...
了解 DDD 吗?DDD 和 MVC 的区别是什么?
简介: DDD(Domain-driven Design) 和 MVC(Model-View-Controller) 是软件后台开发两种流行的分层架构思想。 MVC 是一种设计模式,主要用来分离用户界面,业务逻辑,和数据模型。 而…...

Unity3D仿星露谷物语开发46之种植/砍伐橡树
1、目标 种植一棵橡树,从种子变成大树。 然后可以使用斧头砍伐橡树。 2、删除totalGrowthDays字段 修改growthDays的含义,定义每个值为到达当前阶段的累加天数。此时最后一个阶段就是totalGrowthDays的含义。所以就可以删除totalGrowthDays字段。 &…...
STM32外设应用详解——从基础到高级应用的全面指南
目录 一、引言:为何选择STM32外设 二、主要外设类别与详细应用解析 1. GPIO(通用输入输出) 工作原理详解 高级应用设计 硬件连接建议 2. 定时器(TIM)详解 基本定时器原理 高级配置 实际应用 核心技巧 3. A…...
作业帮C++后台开发面试题及参考答案
Cookie 和 Session 的区别是什么? Cookie 和 Session 是 Web 开发中用于管理用户状态的两种机制,它们在存储位置、安全性、生命周期和数据类型等方面存在显著差异。 存储位置:Cookie 数据存储在客户端浏览器,而 Session 数据存储在服务器端。当浏览器向服务器发送请求时,…...
红队进阶实战
4.1 内网渗透(域渗透、横向移动) 域环境攻击链 初始立足点:通过钓鱼获取域用户凭据(如NTLM Hash)。信息收集: 使用BloodHound自动化分析域内关系。执行nltest /dclist:domain.com获取域控制器列表。横向移动: Pass-the-Hash:利用Mimikatz注入Hash到新会话。sekurlsa::…...
C语言中的指定初始化器
什么是指定初始化器? C99标准引入了一种更灵活、直观的初始化语法——指定初始化器(designated initializer), 可以在初始化列表中直接引用结构体或联合体成员名称的语法。通过这种方式,我们可以跳过某些不需要初始化的成员,并且可以以任意顺序对特定成员进行初始化。这…...
C/C++ 整数类型的长度
参考 cppreference.cn 在某些语言中,整数类型的长度是固定的,如java中 char 8short 16int 32long 64 可是C/C 与机器相关,整数类型长度与平台有关 先可以记一个简单的 按照C标准: char > 8short > 16int > 16long &g…...

gRPC开发指南:Visual Studio 2022 + Vcpkg + Windows全流程配置
前言 gRPC作为Google开源的高性能RPC框架,在微服务架构中扮演着重要角色。本文将详细介绍在Windows平台下,使用Visual Studio 2022和Vcpkg进行gRPC开发的完整流程,包括环境配置、项目搭建、常见问题解决等实用内容。 环境准备 1. 安装必要组…...

高密度服务器机柜散热方案:高风压风机在复杂风道中的关键作用与选型要点
随着云计算、人工智能等技术的飞速发展,数据中心内服务器机柜的集成度不断攀升,高密度部署成为常态。然而,高密度意味着单位空间内服务器数量剧增,发热量呈指数级上升,传统散热方案已难以满足需求。在复杂的机柜风道环…...
Android framework 问题记录
一、休眠唤醒,很快熄屏 1.1 问题描述 机器休眠唤醒后,没有按照约定的熄屏timeout 进行熄屏,很快就熄屏(约2s~3s左右) 1.2 原因分析: 抓取相关log,打印休眠背光 相关调用栈 //具体打印调用栈…...

框架之下再看HTTP请求对接后端method
在当今的软件开发中,各类框架如雨后春笋般不断涌现,极大地提升了开发效率。以 Java 开发为例,Spring 框架历经多次迭代演进,而 Spring Boot 更是将开发便捷性提升到了新高度。如今,开发者只需简单引入 Maven 包&#x…...
Oracle APEX IR报表列宽调整
目录 1. 问题:如何调整Oracle APEX IR报表列宽 2. 解决办法 1. 问题:如何调整Oracle APEX IR报表列宽 1-1. 防止因标题长而数据短,导致标题行的文字都立起来了,不好看。 1-2. 防止因数据太长而且中间还没有空格,把列…...

【笔记】与PyCharm官方沟通解决开发环境问题
#工作记录 2025年5月20日 星期二 背景 在此前的笔记中,我们提到了向PyCharm官方反馈了几个关于Conda环境自动激活、远程解释器在社区版中的同步问题以及Shell脚本执行时遇到的问题。这些问题对日常开发流程产生了一定影响,因此决定联系官方支持寻求解…...
深入解析:如何基于开源OpENer开发EtherNet/IP从站服务
一、EtherNet/IP协议概述 EtherNet/IP(Industrial Protocol)是一种基于以太网的工业自动化通信协议,它将CIP(Common Industrial Protocol)封装在标准以太网帧中,通过TCP/IP和UDP/IP实现工业设备间的通信。作为ODVA(Open DeviceNet Vendors Association)组织的核心协议…...

node.js文件系统(fs) - 创建文件、打开文件、写入数据、追加数据、读取数据、创建目录、删除目录
注意:以下所有示例均是异步语法! 注意:以下所有示例均是异步语法! 创建文件 node.js 允许我们在计算机本地创建文件,例如创建一个 word 文件: // 引入核心模块(fs) var fs require(fs)// API fs.writeF…...
SQL:MySQL函数:空值处理函数(NULL Handling Functions)
目录 什么是空值(NULL)? 常用空值处理函数总览 1️⃣ IFNULL() – 空值替换函数(If Null) 2️⃣ COALESCE() – 多参数空值判断(返回第一个非 NULL 值) 3️⃣ NULLIF() – 相等则返回 NULL…...

利用ffmpeg截图和生成gif
从视频中截取指定数量的图片 ffmpeg -i input.mp4 -ss 00:00:10 -vframes 1 output.jpgffmpeg -i input.mp4 -ss 00:00:10 -vframes 180 output.jpg -vframes 180代表截取180帧, 实测后发现如果视频是60fps,那么会从第10秒截取到第13秒-i input.mp4:指定输入视频文…...

初始化一个Springboot项目
初始化一个Springboot项目 文章目录 初始化一个Springboot项目1、新建项目2、配置yml3、自定义异常4、通用相应类5、全局跨域配置6、总结 1、新建项目 首先,我们需要创建一个新的 Spring Boot 项目。这里我们使用 IntelliJ IDEA 作为开发工具,它提供了方…...

YOLOv8在单目向下多车辆目标检测中的应用
大家读完觉得我有帮助记得关注!!! 摘要 自动驾驶技术正逐步改变传统的汽车驾驶方式,标志着现代交通运输的一个重要里程碑。目标检测是自主系统的基石,在提高驾驶安全性、实现自主功能、提高交通效率和促进有效的应急…...
23种设计模式解释+记忆
一、创建型模式(5种)—— “怎么造对象?” 单例模式(Singleton) 场景:公司的CEO只能有一个。 核心:确保一个类只有一个实例,全局访问。 关键词:唯一、全局访问。 工厂方…...

Baklib构建AI就绪型知识中台实践
Baklib驱动企业知识资产重构 在数字化转型浪潮中,企业知识中台的构建已成为激活数据价值的关键路径。Baklib通过结构化存储与智能分类引擎,将分散于邮件、文档、IM工具中的碎片化信息转化为可检索、可复用的数字资产。其核心能力体现在三个维度…...

JS逆向-某易云音乐下载器
文章目录 介绍下载链接Robots文件搜索功能JS逆向**函数a:生成随机字符串****函数b:AES-CBC加密****函数c:RSA公钥加密** 歌曲下载总结 介绍 在某易云音乐中,很多歌曲听是免费的,但下载需要VIP,此程序旨在“…...
FreeRTOS全攻略:从入门到精通
目录 一、FreeRTOS 基础概念1.1 FreeRTOS 是什么1.2 为什么选择 FreeRTOS 二、与裸机开发的区别2.1 任务管理2.2 中断处理2.3 资源管理 三、FreeRTOS 入门篇3.1 内存管理3.2 任务创建3.3 任务状态3.4 任务优先级3.5 空闲任务和钩子函数3.6 同步与互斥3.7 队列3.8 信号量3…...

服务器的基础知识
什么是服务器 配置牛、运行稳、价格感人的高级计算机,家用电脑不能比拟的。 服务器的组成:电源、raid卡、网卡、内存、cpu、主板、风扇、硬盘。 服务器的分类 按计算能力分类 超级计算机 小型机AIX x86服务器(服务器cpu架构) …...
AGI大模型(25):LangChain提示词模版
我们也可以创建prompt template, 并引入一些变量到prompt template中,这样在应用的时候更加灵活。 1 代码实现 # 我们也可以创建prompt template, 并引入一些变量到prompt template中,这样在应用的时候更加灵活 from langchain_core.prompts import ChatPromptTemplate from…...

Python连接redis
第一步安装redis Releases microsoftarchive/redis 安装时勾上所有能勾上的选项下一步即可 在CMD中pip install redis 安装redis pip install redis -i https://pypi.tuna.tsinghua.edu.cn/simple 配置redis 在redis安装目录下找到 修改 line 57 bind 0.0.0.0 line…...

使用exceljs将excel文件转化为html预览最佳实践(完整源码)
前言 在企业应用中,我们时常会遇到需要上传并展示 Excel 文件的需求,以实现文件内容的在线预览。经过一番探索与尝试,笔者最终借助 exceljs 这一库成功实现了该功能。本文将以 Vue 3 为例,演示如何实现该功能,代码示例…...