SQL次日留存率计算精讲:自连接与多字段去重的深度应用

news2025/5/23 9:48:54

一、问题拆解:理解次日留存率的计算逻辑

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:作为关联表,表示"第二天刷题记录"

连接条件解析

  1. q1.device_id = q2.device_id:确保关联同一用户的记录
  2. DATEDIFF(q2.date, q1.date) = 1:确保q2的日期比q1晚一天

左连接的意义

  • 即使某用户在次日没有刷题记录(q2为NULL),q1的记录仍会被保留
  • 这保证了分母(所有第一天刷题用户)的完整性

三、COUNT(DISTINCT …) 多字段去重详解

3.1 多字段去重的内在逻辑

COUNT(DISTINCT q2.device_id, q2.date)

执行步骤

  1. 组合键生成:将device_iddate组合成复合键(如1001-2023-01-02
  2. 哈希去重:数据库内部使用哈希表对组合键进行去重
  3. 计数统计:统计去重后的组合键数量

与单字段去重的区别

表达式统计逻辑
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_iddate
10012023-01-01
10022023-01-01
10032023-01-01

q2表(第二天刷题记录)

device_iddate
10012023-01-02
10012023-01-02

自连接结果

q1.device_idq1.dateq2.device_idq2.date
10012023-01-0110012023-01-02
10022023-01-01NULLNULL
10032023-01-01NULLNULL

4.2 统计过程详解

  1. 分子计算

    • COUNT(DISTINCT q2.device_id, q2.date) = 1
    • 去重后只有(1001, 2023-01-02)这一个有效组合
  2. 分母计算

    • COUNT(DISTINCT q1.device_id, q1.date) = 3
    • 包含(1001, 2023-01-01)(1002, 2023-01-01)(1003, 2023-01-01)
  3. 结果

    • 次日留存率 = 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列:理想情况为refrange,避免ALL(全表扫描)
  • key列:应显示使用了idx_device_date索引
  • Extra列:避免出现Using temporaryUsing 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 核心技术点回顾

  1. 自连接技术:通过表别名实现同一表的不同时间关联
  2. COUNT(DISTINCT):多字段组合去重统计的关键
  3. LEFT JOIN:确保分母统计的完整性,包含所有可能留存的用户
  4. 索引优化:复合索引显著提升大数据量下的查询性能

7.2 技术决策树

开始
│
├── 是否需要统计用户行为留存率?
│   │
│   └── 是 → 是否需要多日留存?
│       │
│       ├── 是 → 使用DATEDIFF调整时间窗口
│       │
│       └── 否 → 是否需要去重?
│           │
│           ├── 是 → 使用COUNT(DISTINCT ...)
│           │
│           └── 否 → 直接使用COUNT
│
├── 是否存在性能问题?
│   │
│   └── 是 → 创建复合索引(用户ID, 日期)
│
└── 结束

通过深入理解自连接和多字段去重的原理,结合索引优化技术,我们可以高效、准确地计算各种时间窗口的用户留存率。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2381966.html

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

相关文章

使用SQLite Studio导出/导入SQL修复损坏的数据库

使用SQLite Studio导出/导入SQL修复损坏的数据库 使用Zotero时遇到了数据库损坏,在软件中寸步难行,遂尝试修复数据库。 一、SQLite Studio简介 SQLite Studio是一款专为SQLite数据库设计的免费开源工具,支持Windows/macOS/Linux。相较于其…

Unity3D仿星露谷物语开发46之种植/砍伐橡树

1、目标 种植一棵橡树,从种子变成大树。 然后可以使用斧头砍伐橡树。 2、删除totalGrowthDays字段 修改growthDays的含义,定义每个值为到达当前阶段的累加天数。此时最后一个阶段就是totalGrowthDays的含义。所以就可以删除totalGrowthDays字段。 &…

gRPC开发指南:Visual Studio 2022 + Vcpkg + Windows全流程配置

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

高密度服务器机柜散热方案:高风压风机在复杂风道中的关键作用与选型要点

随着云计算、人工智能等技术的飞速发展,数据中心内服务器机柜的集成度不断攀升,高密度部署成为常态。然而,高密度意味着单位空间内服务器数量剧增,发热量呈指数级上升,传统散热方案已难以满足需求。在复杂的机柜风道环…

框架之下再看HTTP请求对接后端method

在当今的软件开发中,各类框架如雨后春笋般不断涌现,极大地提升了开发效率。以 Java 开发为例,Spring 框架历经多次迭代演进,而 Spring Boot 更是将开发便捷性提升到了新高度。如今,开发者只需简单引入 Maven 包&#x…

【笔记】与PyCharm官方沟通解决开发环境问题

#工作记录 2025年5月20日 星期二 背景 在此前的笔记中,我们提到了向PyCharm官方反馈了几个关于Conda环境自动激活、远程解释器在社区版中的同步问题以及Shell脚本执行时遇到的问题。这些问题对日常开发流程产生了一定影响,因此决定联系官方支持寻求解…

node.js文件系统(fs) - 创建文件、打开文件、写入数据、追加数据、读取数据、创建目录、删除目录

注意:以下所有示例均是异步语法! 注意:以下所有示例均是异步语法! 创建文件 node.js 允许我们在计算机本地创建文件,例如创建一个 word 文件: // 引入核心模块(fs) var fs require(fs)// API fs.writeF…

利用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在单目向下多车辆目标检测中的应用

大家读完觉得我有帮助记得关注!!! 摘要 自动驾驶技术正逐步改变传统的汽车驾驶方式,标志着现代交通运输的一个重要里程碑。目标检测是自主系统的基石,在提高驾驶安全性、实现自主功能、提高交通效率和促进有效的应急…

Baklib构建AI就绪型知识中台实践

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

JS逆向-某易云音乐下载器

文章目录 介绍下载链接Robots文件搜索功能JS逆向**函数a:生成随机字符串****函数b:AES-CBC加密****函数c:RSA公钥加密** 歌曲下载总结 介绍 在某易云音乐中,很多歌曲听是免费的,但下载需要VIP,此程序旨在“…

服务器的基础知识

什么是服务器 配置牛、运行稳、价格感人的高级计算机,家用电脑不能比拟的。 服务器的组成:电源、raid卡、网卡、内存、cpu、主板、风扇、硬盘。 服务器的分类 按计算能力分类 超级计算机 小型机AIX x86服务器(服务器cpu架构) …

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 为例,演示如何实现该功能,代码示例…

前端面经12 函数柯里化

<script>function sum(num){return function(num2){return numnum2}}console.log(sum(1)(2))</script>面试考察 只要参数够了 达到某个数量就输出 <script>let nums[]function sum(...args){nums.push(...args)if(nums.length>5){const out (nums.slice…

告别蜘蛛池!PHP 打造你的网站专属蜘蛛导航仪

在网站优化的赛道上&#xff0c;吸引搜索引擎蜘蛛来访一直是站长和开发者关注的重点。以往借助蜘蛛池、软件等工具引蜘蛛&#xff0c;不仅存在成本高、易违规的风险&#xff0c;效果也参差不齐。现在&#xff0c;有一种更高效、更安全的方式 —— 利用 PHP 代码&#xff0c;无需…

ubuntu kubeasz 部署高可用k8s 集群

ubuntu kubeasz 部署高可用k8s 集群 测试环境主机列表软件清单kubeasz 部署高可用 kubernetes配置源配置host文件安装 ansible 并进行 ssh 免密登录:下载 kubeasz 项⽬及组件部署集群部署各组件开始安装修改 config 配置文件增加 master 节点增加 kube_node 节点登录dashboard…

芯驰科技与安波福联合举办技术研讨会,深化智能汽车领域合作交流

5月15日&#xff0c;芯驰科技与全球移动出行技术解决方案供应商安波福&#xff08;Aptiv&#xff09;在上海联合举办以“芯智融合&#xff0c;共赢未来”为主题的技术研讨会。会上&#xff0c;双方聚焦智能座舱与智能车控的发展趋势&#xff0c;展开深入交流与探讨&#xff0c;…

【论文#目标检测】End-to-End Object Detection with Transformers

目录 摘要1.引言2.相关工作2.1 集合预测2.2 Transformer和并行解码2.3 目标检测 3.DETR模型3.1 目标检测集合预测损失3.2 DETR架构 4.实验4.1 与Faster R-CNN的比较4.2 消融研究4.3 分析4.4 DETR用于全景分割 5.结论6.致谢 Author: Nicolas Carion, Francisco Massa, Gabriel S…