首页 > 数据库 > 数据库开发技术 > 游戏行业实战案例 4 :在线时长分析

【面试题】某游戏数据后台设有「登录日志」和「登出日志」两张表。
「登录日志」记录各玩家的登录时间和登录时的角色等级。

「登出日志」记录各玩家的登出时间和登出时的角色等级。

其中,「角色id」字段唯一识别玩家。
游戏开服前两天( 2022-08-13 至 2022-08-14 )的角色登录和登出日志如下


一天中,玩家可以多次登录登出游戏,请使用 SQL 分析出以下业务问题:
请根据玩家登录登出的时间,统计各玩家每天总在线时长情况。
(如玩家登录后没有对应的登出日志,可以使用当天 23:59:59 作为登出时间,时间之间的计算可以考虑使用时间戳函数 unix_timestamp 。)
问题 4 :
#对每个玩家每天的登录时间进行排序select 角色id,日期,登录时间,rank() over(partition by 角色id,日期 order by 登录时间 asc) as 登录排名from 登录日志;#对每个玩家每天的登出时间进行排序select 角色id,日期,登出时间,rank() over(partition by 角色id,日期 order by 登出时间 asc) as 登出排名from 登出日志;
查询结果如下:


select a.角色id,a.日期,a.登录时间,b.登出时间from(select 角色id,日期,登录时间,rank() over(partition by 角色id,日期 order by 登录时间 asc) as 登录排名from 登录日志) as aleft join(select 角色id,日期,登出时间,rank() over(partition by 角色id,日期 order by 登出时间 asc) as 登出排名from 登出日志) as bon a.角色id = b.角色id and a.日期 = b.日期 and a.登录排名 = b.登出排名;
查询结果如下:

case when 登出时间 is null then 当天23:59:59 else 登出时间 endifnull(值1,值2)其中,若值 1 为 null ,则返回值 2 ,若值 1 不为 null ,则返回值 1 。
比如:
ifnull(null,1) ,返回值为 1 ;ifnull(0,1) ,返回值为 0 。
将其应用于本问题,则是:
ifnull(登出时间,'当天23:59:59')concat(日期,' 23:59:59')这样,在左联结时,同时填充「登出时间」字段空值的 SQL 的书写方法为:
select a.角色id,a.日期,a.登录时间,(case when b.登出时间 is null then concat(a.日期,'23:59:59') else b.登出时间 end) as 登出时间#使用ifnull()函数,则为ifnull(b.登出时间,concat(a.日期,' 23:59:59')) as 登出时间from(select 角色id,日期,登录时间,rank() over(partition by 角色id,日期 order by 登录时间 asc) as 登录排名from 登录日志) as aleft join(select 角色id,日期,登出时间,rank() over(partition by 角色id,日期 order by 登出时间 asc) as 登出排名from 登出日志) as bon a.角色id = b.角色id and a.日期 = b.日期 and a.登录排名 = b.登出排名;
查询结果如下:

select 角色id,日期,unix_timestamp(登出时间) - unix_timestamp(登录时间) as 每次在线时长from c;
unix_timestamp() 函数可以将日期时间格式转化成 10 位数的时间戳格式,单位为秒,因此,为了得到单位为分钟的在线时长,我们需要在登出登录时间相减后再除以 60 秒,即:
select 角色id,日期,(unix_timestamp(登出时间) - unix_timestamp(登录时间))/60 as 每次在线时长_minfrom c;
利用 with…as 语句来封装临时表 c 的查询语句,则 SQL 的书写方法:
with c as(select a.角色id,a.日期,a.登录时间,(case when b.登出时间 is null then concat(a.日期,'23:59:59') else b.登出时间 end) as 登出时间from(select 角色id,日期,登录时间,rank() over(partition by 角色id,日期 order by 登录时间 asc) as 登录排名from 登录日志) as aleft join(select 角色id,日期,登出时间,rank() over(partition by 角色id,日期 order by 登出时间 asc) as 登出排名from 登出日志) as bon a.角色id = b.角色id and a.日期 = b.日期 and a.登录排名 = b.登出排名)select 角色id,日期,round((unix_timestamp(登出时间)- unix_timestamp(登录时间))/60,2) as 每次在线时长_min #使用round()函数保留2位小数from c;
查询结果如下:

with c as(select a.角色id,a.日期,a.登录时间,(case when b.登出时间 is null then concat(a.日期,'23:59:59') else b.登出时间 end) as 登出时间from(select 角色id,日期,登录时间,rank() over(partition by 角色id,日期 order by 登录时间 asc) as 登录排名from 登录日志) as aleft join(select 角色id,日期,登出时间,rank() over(partition by 角色id,日期 order by 登出时间 asc) as 登出排名from 登出日志) as bon a.角色id = b.角色id and a.日期 = b.日期 and a.登录排名 = b.登出排名)select 角色id,日期,sum(round((unix_timestamp(登出时间)- unix_timestamp(登录时间))/60,2)) as 总在线时长_min #使用round()函数保留2位小数from cgroup by 角色id,日期;
查询结果如下:


⬇️点击「阅读原文」
免费报名 数据分析训练营
原文作者:猴子数据分析;如有侵权,请联系管理员删除。