基于MySQL进行留存率的计算

前言

在对用户进行行为分析时,经常需要计算用户在软件当中的留存率的情况,如果用Python实现,是比较容易的,那如何用SQL进行计算呢,本文将完成这个任务,在编写SQL前,我们需要了解什么事留存率,这是如何计算的。百度百科对于留存率是这样定义的:
在这里插入图片描述
它的计算方法
在这里插入图片描述
如果以某一日期为基准,那么新增用户数就是当天有登录行为的所有用户数,在计算第N日留存数,即应该是该日期有登录行为的用户中在基准日期中也登录过的数量。

实现方法

本文提供两种实现方式,一种为单日期的,一种为多日期的,虽然方法较为类似,但多日期的需要考虑更多。

单日期实现

留存数本身的定义就用户的在当日的登陆数量,所以只要让每一个日期与基准日进行差值处理,过滤掉不在基准日的这些用户数就可以了,根据差值分组计数即为N日留存。
在这里插入图片描述

-- member-id为用户唯一标识
-- create_time为用户登陆时间,一个用户可以存在多次登陆记录
-- 以2019-09-01为基准,计算一天的1到30日留存数
SELECT retain_day, COUNT(member_id)
FROM (
-- 用户在当天可以有多次登陆行为,所以需要distinct
	SELECT DISTINCT member_id, DATE_FORMAT(create_time, '%Y-%m-%d') AS date1
		, DATEDIFF(create_time, '2019-09-01') AS retain_day
	FROM bss_memb_active
	WHERE DATE_FORMAT(create_time, '%Y-%m-%d') BETWEEN '2019-09-01' AND '2019-09-30'
-- 需要去除不在基准日的数据
		AND member_id IN (
			SELECT DISTINCT member_id
			FROM bss_memb_active
			WHERE DATE_FORMAT(create_time, '%Y-%m-%d') = '2019-09-01'
		)
) aa
GROUP BY retain_day

结果
在这里插入图片描述

多日期的留存数

多日期的最终的结果呈现更像我们在其他分析软件上看到的那样,如友盟
在这里插入图片描述
多日期这里我实现的方式是用了表之间的笛卡尔积,MySQL的笛卡尔积可以直接用join不加条件的连接(不清楚笛卡尔积的连接方式可以去这里),将用户登陆的数据在所有需要展示的日期上重复一次,在去除非基准日的用户时,将外层的基准日期带入in的相关子查询中做一次过滤。
在这里插入图片描述
可以这样理解,笛卡尔积产生的每一个基准日的表计算该基准日的N日留存数。

-- sql多次使用了日期,所以这里将日期定义为一个变量
-- row_min_date为结果行中的最小日期
set @row_min_date='2019-09-01';
-- row_min_date为结果行中的最大日期
set @row_max_date='2019-09-10';
SELECT b.date1
-- 日期与基准日的差值即为N日留存数的N,对差值进行基准日的的分组计数,即是结果
	, COUNT(if(DATEDIFF(a.date, b.date1) = 0, a.member_id, NULL)) AS '当天活跃数'
	, COUNT(if(DATEDIFF(a.date, b.date1) = 1, a.member_id, NULL)) AS '第二天活跃数'
	, COUNT(if(DATEDIFF(a.date, b.date1) = 2, a.member_id, NULL)) AS '第三天活跃数'
	, COUNT(if(DATEDIFF(a.date, b.date1) = 3, a.member_id, NULL)) AS '第四天活跃数'
	, COUNT(if(DATEDIFF(a.date, b.date1) = 4, a.member_id, NULL)) AS '第五天活跃数'
	, COUNT(if(DATEDIFF(a.date, b.date1) = 5, a.member_id, NULL)) AS '第六天活跃数'
	, COUNT(if(DATEDIFF(a.date, b.date1) = 6, a.member_id, NULL)) AS '第七天活跃数'
FROM (
	SELECT DISTINCT DATE_FORMAT(create_time, '%Y-%m-%d') AS date, member_id
	FROM bss_memb_active
	WHERE DATE_FORMAT(create_time, '%Y-%m-%d') BETWEEN @row_min_date AND @row_max_date
) a
--笛卡尔积连接
	JOIN (
		SELECT DISTINCT DATE_FORMAT(create_time, '%Y-%m-%d') AS date1
		FROM bss_memb_active
		WHERE DATE_FORMAT(create_time, '%Y-%m-%d') BETWEEN @row_min_date AND @row_max_date
	) b
-- 排除非基准日的用户
WHERE member_id IN (
	SELECT member_id
	FROM bss_memb_active
	WHERE DATE_FORMAT(create_time, '%Y-%m-%d') = b.date1
)
GROUP BY b.date1

结果
在这里插入图片描述

尾记

经历了一段时间对于MySQL的使用,发现MySQL的计算相对于SQL Server来说更加灵活,比如在select中对于字段的处理有更加丰富的函数,但是也相应会有不适应的地方,比如MySQL对于Group by的处理不严谨,不需要聚合函数就能使用,或许有更强大的功能等着我去挖掘,学习永无止境。

### 使用SQL窗口函数计算用户留存率 #### 背景说明 用户留存率是指在特定时间段内注册的用户,在后续某一天仍然活跃的比例。通过窗口函数可以高效地实现这一目标,因为窗口函数允许我们在不破坏原始数据结构的情况下执行复杂的聚合操作。 以下是基于窗口函数计算每日新增用户及其次日、三日乃至更长时间维度下的留存率的方法: --- #### 数据准备 假设存在一张 `tb_user` 表记录用户的注册时间和登录时间,其字段如下: - `id`: 用户唯一标识符 - `register_time`: 注册时间 - `login_time`: 登录时间 为了便于分析,我们需要创建一个辅助列来标记每位用户的首次登录日期。这可以通过以下 SQL 实现[^3]: ```sql WITH first_login AS ( SELECT id, register_time, MIN(login_time) OVER (PARTITION BY id ORDER BY login_time) AS first_login FROM tb_user ) SELECT * FROM first_login; ``` 此部分利用了窗口函数 `MIN()` 和 `OVER(PARTITION BY ...)` 来为每个用户分配首个登录时间作为基准。 --- #### 留存率核心逻辑 接下来,我们将计算每日新增用户数以及他们在不同天数后的留存情况。具体方法分为以下几个方面: 1. **统计每日新增用户数** 利用 `COUNT()` 函数按注册日期分组计数新用户数量: ```sql WITH daily_new_users AS ( SELECT DATE(register_time) AS reg_date, COUNT(*) AS new_users_count FROM tb_user GROUP BY DATE(register_time) ) ``` 2. **匹配未来若干天内的活跃状态** 将每名用户的首次登录日期与其未来的登录行为关联起来。例如,要判断某个用户是否在注册后第 N 天有活动,则需满足条件: ```sql DATEDIFF(login_time, register_time) = N ``` 这里使用 MySQL 的内置函数 `DATEDIFF()` 计算两个日期之间的差值。 3. **应用窗口函数计算留存比例** 结合以上两步的结果,我们可以进一步构建完整的留存率报表。下面是一个综合示例,展示如何计算每日新增用户及其次日、三日和七日的留存率[^4]: ```sql WITH retention_data AS ( SELECT u.id, DATE(u.register_time) AS reg_date, CASE WHEN EXISTS( SELECT 1 FROM tb_user lu WHERE lu.id = u.id AND DATEDIFF(lu.login_time, u.register_time) = 1 ) THEN 1 ELSE 0 END AS day_1_retention, CASE WHEN EXISTS( SELECT 1 FROM tb_user lu WHERE lu.id = u.id AND DATEDIFF(lu.login_time, u.register_time) = 2 ) THEN 1 ELSE 0 END AS day_2_retention, CASE WHEN EXISTS( SELECT 1 FROM tb_user lu WHERE lu.id = u.id AND DATEDIFF(lu.login_time, u.register_time) = 7 ) THEN 1 ELSE 0 END AS day_7_retention FROM tb_user u ), aggregated_data AS ( SELECT reg_date, SUM(day_1_retention) / COUNT(*)::FLOAT AS day_1_rate, SUM(day_2_retention) / COUNT(*)::FLOAT AS day_2_rate, SUM(day_7_retention) / COUNT(*)::FLOAT AS day_7_rate FROM retention_data GROUP BY reg_date ) SELECT * FROM aggregated_data; ``` 在此过程中,我们分别定义了一个子查询用于逐条检测各用户是否存在对应天数上的登录动作,并最终汇总成整体比率。 --- #### 总结 上述方案充分运用了 SQL 中强大的窗口功能以及其他高级特性(如 CTEs 和条件表达式)。它不仅能够清晰呈现每一天的新用户增长趋势,还提供了关于这些用户长期参与度的重要指标——即他们的留存表现。 ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值