数据运营常见问题之留存率/连续登陆等详解(SQL & Hive)

 更新时间:2026年04月04日 08:55:22   作者:ckSpark  
在互联网行业中,用户在某段时间内开始使用应用,经过一段时间后,仍然继续使用该应用的用户,被认作是留存用户这篇文章主要介绍了SQL&Hive数据运营常见问题之留存率/连续登陆等的相关资料,需要的朋友可以参考下

一、问题:留存率计算(SQL)

字段及表说明:
表名:user_log
字段名:
log_day:登录日期
device_id:用户设备id
app_id:用户app的id,其中device_id和app_id确定唯一的用户

1.1计算某日留存率(次日、3日、7日、30日)

--计算次日、3日、7日、30日留存率
select a.log_day 首次登录日期,
count(user_id_d1)/count(user_id_d0) retention_d1,
count(user_id_d3)/count(user_id_d0) retention_d3,
count(user_id_d7)/count(user_id_d0) retention_d7,
count(user_id_d30)/count(user_id_d0) retention_d30 from(
--找出新增
select distinct log_day,device_id||app_id user_id_d0 from user_log a where log_day=log_day'day' 
and device_id||app_id not in(select distinct device_id||app_id from user_log b
	where b.log_day<a.log_day)
)a
left join (select distinct log_day,device_id||app_id user_id_d1 from user_log where log_day=(log_day'day'+1) b on a.user_id_d0=b.user_id_d1
left join (select distinct log_day,device_id||app_id user_id_d3 from user_log where log_day=(log_day'day'+3) c on a.user_id_d0=c.user_id_d3
left join (select distinct log_day,device_id||app_id user_id_d7 from user_log where log_day=(log_day'day'+7) d on a.user_id_d0=d.user_id_d7
left join (select distinct log_day,device_id||app_id user_id_d30 from user_log where log_day=(log_day'day'+30) e on a.user_id_d0=e.user_id_d30
group by a.log_day

1.2计算每日留存率(次日、3日、7日、30日)

-- 创建留存率储存表
create table user_retention_monitior(
log_day date,retention_d1 number,retention_d3 number,
retention_d7 number,retention_d30 number
);
-- 清空表
truncate table user_retention_monitior;
declare
day date;    --变量声明
-- 程序主体
begin
select min(log_day) into day from user_log;   --变量赋初始值
loop
	insert into user_retention_monitior
	-- 计算留存
	select a.log_day,
	count(b.user_id_d1)/count(a.user_id_d0) retention_d1,
	count(c.user_id_d3)/count(a.user_id_d0) retention_d3,
	count(d.user_id_d7)/count(a.user_id_d0) retention_d7,
	count(e.user_id_d30)/count(a.user_id_d0) retention_d30
	 from(
	select distinct log_day,app_id||device_id user_id_d0 from user_log a where log_day=log_day'day' 
	and app_id||device_id not in( select distinct pp_id||device_id from user_log
		where log_day<log_day'day' )
	)a left join(
	select distinct log_day,app_id||device_id user_id_d1 from user_log where log_day=(log_day'day'+1)
	)b on a.user_id_d0=b.user_id_d1 left join(
	select distinct log_day,app_id||device_id user_id_d3 from user_log where log_day=(log_day'day'+3)
	)c on a.user_id_d0=c.user_id_d3 left join(
	select distinct log_day,app_id||device_id user_id_d7 from user_log where log_day=(log_day'day'+7)
	)d on a.user_id_d0=d.user_id_d7 left join(
	select distinct log_day,app_id||device_id user_id_d30 from user_log where log_day=(log_day'day'+30)
	)e on a.user_id_d0=e.user_id_d30
	group by a.log_day;
	commit;
	day:=day+1;  --循环
	exit when day>=truc(sysdate);   --结束条件
end loop;
end;
select * from user_retention_monitior;

二、问题:连续登陆问题(Hive语法)

基础数据准备:

1. 表结构
表名: user_login
字段名:
user_id string comment '用户id'
login_dt string comment '用户登陆时间'
2. 数据格式
user_id login_dt
1       2020-05-01
1       2020-05-02
1       2020-05-04
3. 格式转换(login_dt由字符串格式转为时间格式)
to_date(from_unixtime(UNIX_TIMESTAMP(login_dt,'yyyy-MM-dd'))) as login_dt

连续登陆计算的两个思路:

①可先找出最近几天的日期,然后计算与当期日期的差值。

相应函数:lead/lag + datediff(或date_sub/date_add均可)

LAG(col,n,DEFAULT) 用于统计窗口内往上第n行值
第一个参数为列名,第二个参数为往上第n行(可选,默认为1),第三个参数为默认值(当往上第n行为NULL时候,取默认值,如不指定,则为NULL)
LEAD(col,n,DEFAULT) 用于统计窗口内往下第n行值

②先对用户按登陆日期排序;根据登陆日期与排序差值进行分组统计。
相应函数:row_number() + date_sub

思路1:

比如求连续三天登陆,可以将当天上一条和下一条数据获取到,然后保证now-lag=lead-now=1即可:

//2. 要求now-lag=lead-now=1
select user_id from(
//1. 获取当天前后各一条数据
select user_id,login_dt,
lag(login_dt,1) over(partition by user_id order by login_dt) as lag_dt_01,
lead(login_dt,1) over(partition by user_id order by login_dt) as lead_dt_01
from user_login)temp1
where datediff(login_dt,lag_dt_01)=1 and datediff(lead_dt_01,login_dt)=1

如果连续多天,可以取更多数据,或者全用lag/lead函数。

思路2:

//2. 将用户根据登陆时间和序号的差值进行分组,得到连续登陆的起止日期、连续登陆的天数
select user_id,
min(login_dt) as start_dt,
max(login_dt) as end_dt,
count(1) as continue_day
 from(
//1. 将用户按登陆时间排序号
select user_id,login_dt,
row_number() over(partition by user_id order by login_dt) as rn from user_login
)temp1
group by user_id,date_sub(login_dt,rn)

若需统计连续大于N天登陆的用户,可结合Having子句进行筛选。
参考链接: 连续登陆问题

三、TOP-N

基础数据准备:

每个顾客访客访问任何一个店铺的任何一个商品时都会产生一条访问日志.
访问日志存储的表名为Visit,访客的用户id为user_id,被访问的店铺名称为shop.
请统计每个店铺访问次数top3的访客信息.输出店铺名称、访客id、访问次数

思路及代码:

// 3. 取前三名
select shop,user_id,visit_num from(
// 2. 统计每个页面各用户的访问次数排序
	select shop,user_id,visit_num,
	row_num() over(partition by shop,user_id order by visit_num desc) as visit_sort
	 from(
		// 1. 统计每个用户每个页面的访问次数
		select shop,user_id,count(1) as visit_num from visit
		group by shop,user_id
		)temp1
)temp2
where visit_sort<=3

四、行列互换

1. 将行中数据(数组),拆分为多行,On-to-many maping问题

基础数据准备:

-----------------------------------------------------
原式数据movie表:
 movie   		 category
《疑犯追踪》		 悬疑,动作,科幻,剧情
《Lie to me》 	 悬疑,警匪,动作,心理,剧情
《战狼 2》 		 战争,动作,灾难
-----------------------------------------------------
结果数据
 movie		category_name
《疑犯追踪》 		悬疑
《疑犯追踪》 		动作
《疑犯追踪》 		科幻
《疑犯追踪》 		剧情
《Lie to me》   	悬疑
《Lie to me》    警匪
《Lie to me》    动作
《Lie to me》    心理
《Lie to me》    剧情
《战狼 2》        战争
《战狼 2》        动作
《战狼 2》        灾难

思路及代码:UDTF函数(User-Defined Table-Generating Functions)与Lateral view结合使用

select movie,category_name from movie
lateral view explode(split(category),',') tmpTable as category_name

2. "多行"转"一行"问题

基础数据准备:

-----------------------------------------------------
原始数据constellation表:
name 	constellation 	blood_type
孙悟空 	白羊座 			A
大海 	射手座 			A
宋宋 	白羊座 			B
猪八戒 	白羊座 			A
凤姐 	射手座 			A
-----------------------------------------------------
结果数据
--把星座和血型一样的人归类到一起。结果如下:
射手座,A 	大海|凤姐
白羊座,A 	孙悟空|猪八戒
白羊座,B 	宋宋

思路及代码:使用group_concat函数

2. 根据新字段进行多行合并:group_concat()
select base,
group_concat(name separator '|') as name
 from(
1. 将星座和血型整合为新字段
select concat(constellation,‘,',blood_type) as base,name from constellation
)temp1
group by base

若没有group_concat()函数,可以用concat_ws(separator,collect_set(column)) 代替。

五、学生选课情况

1. 类似进行透视功能,将列数据透视为矩阵数据

需求描述:

原始数据
----------------course表-----------------
id course 
1   a 
1   b 
1   c 
1   e 
2   a 
2   c 
2   d 
2   f 
3   a 
3   b 
3   c 
3   e
预期结果:表中的1表示选修,表中的0表示未选修
----------------结果展示------------------
id    a    b    c    d    e    f
1     1    1    1    0    1    0
2     1    0    1    1    0    1
3     1    1    1    0    1    0

思路及代码:

  1. 以数组形式整理每个学生选课信息;
  2. 判断数组数据是否在全量数组中。
    collect_set()、array_contains()以及case when实现。
3. 判断选课信息是否可匹配到
select id,
case when array_contains(id_course,course[0]) then 1 else 0 end as a,
case when array_contains(id_course,course[1]) then 1 else 0 end as b,
case when array_contains(id_course,course[2]) then 1 else 0 end as c,
case when array_contains(id_course,course[3]) then 1 else 0 end as d,
case when array_contains(id_course,course[4]) then 1 else 0 end as e,
case when array_contains(id_course,course[5]) then 1 else 0 end as f
 from(
4. 以数组形式整理每个学生的选课信息
select id,id_course,course from(
select id,collect_set(course) as id_course from course
group by id)as temp1 cross join(
select sort_array(collect_set(course)) as course from course
)as temp2)

关于collect_set() 和 array_contains()函数用法:

  1. collect_list():根据某个字段分组后,把分在一组的数据合并在一起,默认分隔符’,’ 。
  2. collect_set():在collect_list()的基础上去重 另:set聚合无序,可以使用sort_array()函数进行排序。
  3. array_sort():对数组内数据进行排序,sort_array(e: column, asc: boolean),默认升序排序。
  4. array_contains():类似于in的用法,array_contains(数组,值) 判断数组中是否有某值。

总结 

到此这篇关于SQL &amp; Hive数据运营常见问题之留存率/连续登陆等详解的文章就介绍到这了,更多相关SQL Hive留存率/连续登陆内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MyISAM和InnoDB引擎优化分析

    MyISAM和InnoDB引擎优化分析

    这几天在学习mysql数据库的优化并在自己的服务器上进行设置,喻名堂主要学习了MyISAM和InnoDB两种引擎的优化方法,需要了解跟多的朋友可以参考下
    2012-11-11
  • phpmyadmin显示MySQL数据表“使用中” 修复后依然无效的解决方法

    phpmyadmin显示MySQL数据表“使用中” 修复后依然无效的解决方法

    这篇文章主要介绍了phpmyadmin显示MySQL数据表“使用中” 修复后依然无效的解决方法,需要的朋友可以参考下
    2014-07-07
  • mysql5.7.18安装时mysql服务启动失败的解决方法

    mysql5.7.18安装时mysql服务启动失败的解决方法

    这篇文章主要为大家详细介绍了mysql5.7.18安装时mysql服务启动失败的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • mysql5.5中文乱码问题解决的有用方法

    mysql5.5中文乱码问题解决的有用方法

    在使用MYSQL时遇到中文乱码的问题,表现为插入数据后查询时输出为??(乱码),下面这篇文章主要给大家介绍了关于mysql5.5中文乱码问题解决的有用方法,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • MySQL中interactive_timeout和wait_timeout的区别

    MySQL中interactive_timeout和wait_timeout的区别

    这篇文章主要介绍了MySQL中interactive_timeout和wait_timeout的区别,非常不错具有参考借鉴价值,需要的朋友可以参考下
    2016-10-10
  • mysql之validate_password_policy的使用

    mysql之validate_password_policy的使用

    这篇文章主要介绍了mysql之validate_password_policy的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • MySQL索引失效场景及解决方案

    MySQL索引失效场景及解决方案

    这篇文章主要介绍了MySQL索引失效场景及解决方案,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-07-07
  • MySQL多版本并发控制MVCC底层原理解析

    MySQL多版本并发控制MVCC底层原理解析

    本文详细讲解了MySQL多版本并发控制MVCC底层原理,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • MySQL的事务和视图使用及说明

    MySQL的事务和视图使用及说明

    事务是数据库中一组SQL语句,要么全部执行成功,要么全部不执行,事务的四个特征为原子性、一致性、持久性和隔离性,隔离性主要解决并发情况下可能出现的脏读、不可重复读和幻读问题,视图是一个虚拟表,根据其他表或视图的查询结果生成,视图可以创建、修改和删除
    2026-01-01
  • 基于MYSQL中优化的一些方法

    基于MYSQL中优化的一些方法

    本篇文章是对MYSQL中优化的一些方法进行了详细的介绍,需要的朋友参考下
    2013-05-05

最新评论