SQL实现次日、三日及七日用户留存率的计算

这篇博客介绍了如何使用SQL在role_login_back表中,基于event_time和device_id字段计算次日、三日及七日用户留存率。留存率定义为:次日留存是当日登录后次日还登录的用户;三日留存是当日登录后第三天登录的用户;七日留存是当日登录后第七天登录的用户。文章提供了计算留存率的步骤和SQL代码。

本篇博客学习如何用SQL来实现次日、三日及七日留存率的计算。

假设有一个表 role_login_back,有字段:event_time表示登陆日期时间和device_id表示设备ID。

event_timedevice_id
2020-01-01 12:40:44cB789hj888888

现在要计算用户的次日留存率、三日留存率、七日留存率等。在计算之前,我们先来弄清楚这些留存的定义。

  • 次日留存:即当日登录后,第二天也登录的用户,称为次日留存用户。
  • 三日留存:即当日登录后,第三天也登录的用户,称为三日留存用户。
  • 七日留存:即当日登录后,第七天也登录的用户,称为七日留存用户。

代码如下:

① 将device_id分组,每个device_id按照时间进行排序。

select 
	distinct date(event_time) as log_day, # 只关心日期,不关注具体的时间。
	device_id as user_id_d0
from role_login_back
group by device_id
order by log_day; a

② 取出次日、第三天以及第7天登录的设备id。

select 
	distinct log_day,
	a.user_id_d0,
	b.device_id as user_id_d1,
	c.device_id as user_id_d3,
	d.device_id as user_id_d7
from a
left join role_login_back b 
on datediff(date(b.event_time),a.log_day) = 1 
and a.user_id_d0 = b.device_id
left join role_login_back c 
on datediff(date(c.event_time), a.log_day) = 2
and a.user_id_d0 = c.device_id
left join role_login_back d
on datediff(date(d.event_time), a.log_day) = 6
and a.user_id_d0 = d.device_id; temp

③ 计算次日、三日及七日留存率。

select
	log_day '日期',
	count(user_id_d0) '新增数量',
	count(user_id_d1) / count(user_id_d0) '次日留存率',
	count(user_id_d3) / count(user_id_d0) '3日留存率',
	count(user_id_d7) / count(user_id_d0) '7日留存率',
from temp
group by log_day;

综合代码:

select
	log_day '日期',
	count(user_id_d0) '新增数量',
	count(user_id_d1) / count(user_id_d0) '次日留存率',
	count(user_id_d3) / count(user_id_d0) '3日留存率',
	count(user_id_d7) / count(user_id_d0) '7日留存率',
from (
	select 
		distinct log_day,
		a.user_id_d0,
		b.device_id as user_id_d1,
		c.device_id as user_id_d3,
		d.device_id as user_id_d7
	from 
		(select 
			distinct date(event_time) as log_day, # 只关心日期,不关注具体的时间。
			device_id as user_id_d0
		from role_login_back
		group by device_id
		order by log_day) a
	left join role_login_back b 
	on datediff(date(b.event_time),a.log_day) = 1 
	and a.user_id_d0 = b.device_id
	left join role_login_back c 
	on datediff(date(c.event_time), a.log_day) = 2
	and a.user_id_d0 = c.device_id
	left join role_login_back d
	on datediff(date(d.event_time), a.log_day) = 6
	and a.user_id_d0 = d.device_id 
	)
group by log_day;
<think>好的,我现在需要帮用户解决如何用SQL计算次日、3日和7日留存率的问题。首先,我得回忆一下留存率的定义。根据用户提供的引用,留存率是第N天活跃用户数除以首日新增用户数,再乘以100%。所以关键在于如何找到每个用户的首次登录日期,然后统计他们在第N天是否再次登录。 用户给的示例中,引用[1]提到使用窗口函数MIN(login_date) OVER (PARTITION BY user_id)来找到每个用户的首次登录日期。这应该是第一步,生成一个包含用户ID、登录日期和首次登录日期的临时表或子查询。接下来需要关联这个表,找到那些在首次登录后第N天有登录记录的用户。 引用[2]和引用[3]中的SQL语句使用了不同的方法,比如左连接和datediff函数来计算日期差。可能需要用到自连接或者左连接来匹配用户后续的登录日期。例如,对于次日留存,需要检查用户是否存在登录日期等于首次登录日期加1天的记录。同样,3日和7日留存则是加3天和7天。 不过,用户可能希望同时计算这三个留存率,而不是分开三次查询。这时候可能需要使用条件聚合,比如在同一个查询中使用多个COUNT DISTINCT CASE WHEN语句,每个对应不同的日期差。例如,datediff(day, first_day, login_date) = 1,3,7的情况分别统计。 另外,需要注意的是日期格式的处理,不同的数据库可能对日期函数有差异,比如MySQL的DATEDIFF和Hive的可能不同。用户提供的例子中,引用[3]使用了Hive的语法,比如create table和insert into的方式,可能用户的环境是Hive或者类似的系统,所以需要确保使用的函数兼容。 还要考虑去重的问题,因为用户可能在同一天多次登录,但表中已经去重,所以每个用户每天只有一条记录。这样在统计时不需要额外处理重复登录的问题。 可能的步骤是: 1. 为每个用户找到首次登录日期。 2. 将用户的每次登录记录与首次登录日期关联。 3. 统计每个首次登录日期的用户数作为分母。 4. 统计每个首次登录日期后第1、3、7天有登录的用户数作为分子。 5. 按首次登录日期分组,计算留存率。 然后,把这些步骤转化为SQL语句。可能需要使用子查询或者公共表表达式(CTE)来处理第一步,然后进行左自连接或者窗口函数来计算后续登录情况。例如,使用LEFT JOIN将用户后续的登录日期与首次登录日期进行匹配,然后根据日期差筛选出符合条件的记录。 在写具体SQL的时候,需要注意日期加减的操作,比如在Hive中使用date_add函数,而在其他数据库可能用不同的方法。例如,首次登录日期加1天得到次日,然后检查是否存在该日期的登录记录。 最后,可能需要将结果按首次登录日期分组,计算每个日期的留存率,同时处理NULL值,避免分母为零的情况。用户可能需要整体平均的留存率,或者按每天分开显示,这需要根据需求调整GROUP BY的字段。 总结来说,SQL的大体结构可能如下: WITH first_login AS ( SELECT user_id, MIN(login_date) AS first_day FROM user_login GROUP BY user_id ) SELECT first_day AS 注册日期, COUNT(DISTINCT f.user_id) AS 新增用户数, COUNT(DISTINCT CASE WHEN DATEDIFF(l.login_date, f.first_day) = 1 THEN f.user_id END) AS 次日留存数, (次日留存数 / 新增用户数) * 100 AS 次日留存率, -- 类似处理3日和7日 FROM first_login f LEFT JOIN user_login l ON f.user_id = l.user_id GROUP BY first_day; 不过可能需要调整,比如确保每个留存数的计算正确,尤其是左连接可能导致NULL值,需要用条件聚合来统计。同时,用户可能需要同时计算三个留存率,所以需要三个不同的条件判断。 另外,引用[2]中的例子使用了多个左连接,可能更复杂,但或许可以借鉴。比如,为每个留存天数单独关联一次,但这样可能效率不高,不如使用一次左连接,然后在条件中判断不同的日期差。 最后,还要测试SQL是否正确,比如在引用[3]的数据集中,检查计算结果是否符合预期。例如,用户bbb在2023-12-01首次登录,次日有登录,所以应计入次日留存。而用户ccc在2023-12-02首次登录,次日没有登录,第三天有登录,所以计入3日留存吗?可能需要明确日期差是严格等于N,还是大于等于1且小于等于N。例如,3日留存是否指第三天或者三天内?通常留存率是精确到第N天,所以日期差等于N。 总之,最终的SQL应该能正确关联每个用户的首次登录日期,并统计他们在第1、3、7天是否有登录,然后计算留存率。</think>### 分步解答 #### 步骤1:构建用户首次登录日期表 ```sql WITH first_login AS ( SELECT user_id, MIN(login_date) AS first_day -- 获取每个用户的首次登录日期 FROM user_login_table GROUP BY user_id ) ``` #### 步骤2:关联原始表并计算留存 ```sql SELECT f.first_day AS 注册日期, COUNT(DISTINCT f.user_id) AS 新增用户数, -- 计算次日留存数 COUNT(DISTINCT CASE WHEN DATEDIFF(day, f.first_day, l.login_date) = 1 THEN l.user_id END) AS 次日留存数, -- 计算3日留存数 COUNT(DISTINCT CASE WHEN DATEDIFF(day, f.first_day, l.login_date) = 3 THEN l.user_id END) AS 三日留存数, -- 计算7日留存数 COUNT(DISTINCT CASE WHEN DATEDIFF(day, f.first_day, l.login_date) = 7 THEN l.user_id END) AS 七日留存数, -- 计算留存率(保留两位小数) ROUND(次日留存数 * 100.0 / NULLIF(新增用户数, 0), 2) AS 次日留存率, ROUND(三日留存数 * 100.0 / NULLIF(新增用户数, 0), 2) AS 三日留存率, ROUND(七日留存数 * 100.0 / NULLIF(新增用户数, 0), 2) AS 七日留存率 FROM first_login f LEFT JOIN user_login_table l ON f.user_id = l.user_id GROUP BY f.first_day; ``` ### 关键点解释 1. **留存率定义** 公式:$$N\text{日留存率} = \frac{\text{第N天活跃用户数}}{\text{首日新增用户数}} \times 100\%$$ 例如次日留存率是首日用户中第二天仍活跃的比例[^1]。 2. **DATEDIFF函数** 通过`DATEDIFF(day, first_day, login_date)`计算登录日期与首次登录日期的间隔天数,严格匹配间隔天数等于1、3、7的案例。 3. **防止除零错误** 使用`NULLIF(新增用户数, 0)`避免首日新增用户数为零时出现计算错误。 4. **LEFT JOIN的作用** 保留所有首日用户,即使他们没有后续登录记录(此时留存数为0)[^2]。 ### 示例验证 假设数据如下(来自引用[3]的测试数据): | user_id | login_date | |---------|-------------| | aaa | 2023-12-01 | | bbb | 2023-12-01 | | bbb | 2023-12-02 | | ccc | 2023-12-02 | | ccc | 2023-12-03 | 计算结果: - 2023-12-01新增用户2人(aaa、bbb) - 次日留存:仅有bbb在2023-12-02登录 → 次日留存率$50\%$ - 3日留存:无用户在2023-12-04登录 → 留存率$0\%$ - 2023-12-02新增用户1人(ccc) - 3日留存:ccc在2023-12-03登录(间隔1天,不满足3日留存条件) → 留存率$0\%$ ### 扩展说明 - **性能优化**:对`user_login_table`建立`(user_id, login_date)`联合索引可加速查询[^3] - **多日留存简化**:若需同时计算1-7日留存,可通过调整`CASE WHEN`条件批量生成 相关问题
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值