首页 > 其他分享 >1097. 游戏玩法分析 V#三种方法 推荐方法3 次方法1最短

1097. 游戏玩法分析 V#三种方法 推荐方法3 次方法1最短

时间:2024-11-04 20:44:15浏览次数:3  
标签:03 1097 玩法 event player date 2016 方法 id

目录

题目和要求

表:Activity 

+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| player_id    | int     |
| device_id    | int     |
| event_date   | date    |
| games_played | int     |
+--------------+---------+
(player_id,event_date)是此表的主键(具有唯一值的列的组合)
这张表显示了某些游戏的玩家的活动情况
每一行表示一个玩家的记录,在某一天使用某个设备注销之前,登录并玩了很多游戏(可能是 0)
 

玩家的 安装日期 定义为该玩家的第一个登录日。

我们将日期 x 的 第一天留存率 定义为:假定安装日期为 X 的玩家的数量为 N ,其中在 X 之后的一天重新登录的玩家数量为 M,M/N 就是第一天留存率,四舍五入到小数点后两位。

编写解决方案,报告所有安装日期、当天安装游戏的玩家数量和玩家的 第一天留存率。

以 任意顺序 返回结果表。

结果格式如下所示。

 

示例 1:

输入:
Activity 表:
+-----------+-----------+------------+--------------+
| player_id | device_id | event_date | games_played |
+-----------+-----------+------------+--------------+
| 1         | 2         | 2016-03-01 | 5            |
| 1         | 2         | 2016-03-02 | 6            |
| 2         | 3         | 2017-06-25 | 1            |
| 3         | 1         | 2016-03-01 | 0            |
| 3         | 4         | 2016-07-03 | 5            |
+-----------+-----------+------------+--------------+
输出:
+------------+----------+----------------+
| install_dt | installs | Day1_retention |
+------------+----------+----------------+
| 2016-03-01 | 2        | 0.50           |
| 2017-06-25 | 1        | 0.00           |
+------------+----------+----------------+
解释:
玩家 1 和 3 在 2016-03-01 安装了游戏,但只有玩家 1 在 2016-03-02 重新登录,所以 2016-03-01 的第一天留存率是 1/2=0.50
玩家 2 在 2017-06-25 安装了游戏,但在 2017-06-26 没有重新登录,因此 2017-06-25 的第一天留存率为 0/1=0.00


1. 题目代码

Create table If Not Exists Activity (player_id int, device_id int, event_date date, games_played int);
Truncate table Activity;
insert into Activity (player_id, device_id, event_date, games_played) values ('1', '2', '2016-03-01', '5');
insert into Activity (player_id, device_id, event_date, games_played) values ('1', '2', '2016-03-02', '6');
insert into Activity (player_id, device_id, event_date, games_played) values ('1', '2', '2016-03-02', '6');
insert into Activity (player_id, device_id, event_date, games_played) values ('1', '2', '2016-03-02', '6');
insert into Activity (player_id, device_id, event_date, games_played) values ('2', '3', '2017-06-25', '1');
insert into Activity (player_id, device_id, event_date, games_played) values ('3', '1', '2016-03-01', '0');
insert into Activity (player_id, device_id, event_date, games_played) values ('3', '4', '2018-07-03', '5');

2. 解题分析图览

方法1: avg条件 无join 代码最短的方法
Select player_id and min(event_date) as 注册时间 from Activity group by player_id Group by 注册时间 Count(*) as installs Calculate avg retention Round the average to 2 decimal places Select 注册时间, installs, Day1_retention
方法2: join avg条件 (join on and 效率很高)
Select player_id and min(event_date) as 注册时间 from Activity group by player_id Select distinct player_id and event_date from Activity Join a1 with a on 注册时间 + 1 day = event_date and a1.player_id = a.player_id Count installs Calculate avg retention Round the average to 2 decimal places Select 注册时间, installs, Day1_retention
方法3: 与方法1一样灵活,但是效率更高
Select player_id and min(event_date) as 注册时间 from Activity group by player_id Select distinct player_id and event_date from Activity Join a1 with a on a1.player_id = a.player_id Calculate sum of datediff(event_date, 注册时间) = 1 Count distinct player_id and 注册时间 Calculate Day1_retention as sum / count Round the result to 2 decimal places Select 注册时间, installs, Day1_retention

3. 难点分析

本题的难点主要在于:

  1. 注册时间的确定:需要为每位用户找到最早的event_date作为注册时间,这通常需要使用聚合函数如MIN

  2. 留存率的计算:计算特定天数后的留存率涉及日期的操作和逻辑判断,需要正确地将注册时间与后续活动日期进行比较。

  3. 优化查询性能:在数据量较大的情况下,如何通过合理的JOIN和聚合来优化查询性能,是实际工作中需要考虑的关键问题。

  4. 数据去重:实际操作中,用户可能在同一天有多次活动记录,因此在计算留存率时需要对数据进行去重处理,以确保准确性。

4. 答案代码以及pretty表格解释

-- 方法1 avg条件 无join 代码最短的方法
with a1 as (
select
    player_id,
       min(event_date) 注册时间
from activity
group by player_id)
-- 以上筛选出注册表
select 注册时间 install_dt,
       count(*) installs,
       round(avg((player_id, 注册时间 + interval 1 day) in (select distinct player_id,event_date from activity)), 2) Day1_retention
-- 这种方法最段,但是因为是子查询遍历查找,不如join的先筛选在查找,推荐使用方法2 这里不distinct也行,因为题目说
-- 依次类推 + interval 3 day 三日留存; + interval 5 day 五日留存       (player_id,event_date)是此表的主键(具有唯一值的列的组合)
from a1                                                           -- 但是我建议一定要distinct因为实际工作操作时可能同天多次登录
group by 注册时间;
+---------+--------+---------------+
|install_dt|installs|Day1_retention|
+---------+--------+---------------+
|2016-03-01|2       |0.50           |
|2017-06-25|1       |0.00           |
+---------+--------+---------------+

-- 方法2 join avg条件 (join on and 效率很高)
512 ms 击败 100.00%;
    
with a1 as (
select
    player_id,
       min(event_date) 注册时间
from activity
group by player_id)
select
    注册时间 install_dt,
    count(*) installs,
    round(avg(a.player_id is not null),2) Day1_retention
from a1
join
    (select distinct player_id,event_date from activity) a-- 我建议一定要distinct因为实际工作操作时可能同天多次登录
        on a1.注册时间+interval 1 day =a.event_date and a1.player_id=a.player_id
group by 注册时间;
+---------+----------+---------+----------+
|player_id|注册时间   |player_id|event_date|
+---------+----------+---------+----------+
|1        |2016-03-01|1        |2016-03-02|
|3        |2016-03-01|null     |null      |
---------------------------------------------------
|2        |2017-06-25|null     |null      |
+---------+----------+---------+----------+

-- 方法3 与方法1一样灵活,但是效率更高
推荐使用本方法 519 ms 击败 95.24%;
    
with a1 as (
select
    player_id,
       min(event_date) 注册时间
from activity
group by player_id)
select
    注册时间 install_dt,
    count(distinct a1.player_id) installs,
    round(
            sum(datediff(event_date,注册时间)=1)
                /
            count(distinct a1.player_id,注册时间)
        ,2) Day1_retention

from a1
join
    (select distinct player_id,event_date from activity) a-- 我建议一定要distinct因为实际工作操作时可能同天多次登录
        on a1.player_id=a.player_id
group by 注册时间;

+---------+----------+---------+----------+
|player_id|注册时间      |player_id|event_date|
+---------+----------+---------+----------+
|1        |2016-03-01|1        |2016-03-01|
|1        |2016-03-01|1        |2016-03-02|
|3        |2016-03-01|3        |2016-03-01|
|3        |2016-03-01|3        |2018-07-03|
---------------------------------------------------
|2        |2017-06-25|2        |2017-06-25|
+---------+----------+---------+----------+

5. 关键总结

  • 公共表表达式 (CTE):使用WITH子句来简化复杂查询,提升代码可读性。

  • 聚合函数:利用MIN函数确定用户的注册时间,通过COUNTAVG计算安装数量和留存率。

  • 日期操作:使用日期函数如DATE_ADDINTERVAL来计算注册后的特定天数,用于留存率的计算。

  • JOIN 与子查询:理解不同的方法如JOIN和子查询在性能和可读性上的差异,选择最优方案。

  • 数据去重:在处理实际数据时,确保对重复记录进行去重,保证计算的准确性。

  • 性能优化:通过合理的查询结构和索引优化,提高SQL查询的执行效率。

  • 留存率计算逻辑:掌握如何根据注册时间和后续活动时间计算不同天数的用户留存率,理解其业务意义。

标签:03,1097,玩法,event,player,date,2016,方法,id
From: https://blog.csdn.net/weixin_74002941/article/details/143458860

相关文章

  • 0基础学Python装饰器封装、类成员与静态方法、魔术方法
    0基础学Python装饰器封装、类成员与静态方法、魔术方法装饰器封装类成员和静态方法实例属性和方法类属性和类方法静态方法使用场景魔术方法定义初始化与表示方法比较运算方法算术运算方法代码演示装饰器封装装饰器(decorators)是一种高阶函数,用于在不修改原有函数或......
  • 如何为PDF文件设置打开密码?推荐3种方法
    在工作中,PDF文档经常被用于分享和存档,但其中的敏感内容也容易被他人查看。为PDF文件设置一个打开密码,是确保文档安全的有效方式。如果小伙伴们不知道如何给PDF文件设置打开密码,可以看看以下3种方法!方法1:使用PDF编辑工具PDF编辑工具都具备多个功能模块,也支持为PDF文件添加密码......
  • spring-framework的StopWatch类详解,每个方法带有具体的例子
    目录简介:StopWatch类的应用场景:StopWatch类提供了以下一些主要方法:start():stop():reset():split():unsplit():getTime():getStartTime():toString():isStarted():isStopped():示例代码:以下是一个使用StopWatch测量代码执行时间的简单示例:以下是一个使用StopWatch类的复杂示例,该示例演......
  • 汽车行业AI知识库搭建指南:重要性+方法
    在汽车行业,大型车企面临着员工众多、价值链长、技术密集和知识传播难等挑战。如何通过有效的知识沉淀与应用,提升各部门协同效率,快速响应客户咨询,降低销售成本,并开启体系化、可持续性的知识管理建设,成为汽车企业发展的关键。一、汽车企业知识管理的现状与挑战汽车企业及......
  • S7-1500PLC通过存储卡清除项目数据的具体方法演示
    S7-1500PLC通过存储卡清除项目数据的具体方法演示存储卡的功能S7-1500使用SIMATIC存储卡作为程序存储器。SIMATIC存储卡主要有以下功能:1、作为CPU的装载存储区,离开存储卡CPU就无法运行。2、可以用于更新S7-1500CPU及集中式IO模块的固件版本。3、读取服务数据......
  • Timing修复的几种方法之setup
     芯冰乐知识星球入口:芯冰乐之前的推文已经提到过D触发器的工作原理及setup和hold的相关介绍。如果没有看过的童鞋可以戳此处进行了解。下面我们看一下setup如果出现了violation是怎么解的。先看一下setup公式。setup_slack=(capture_clock_latency+phase_shift)-(la......
  • Typora 破解方法(亲测可行)
    第一步下载并安装Typora[点击访问Typora官网]第二步修改文件内容找到以下路径下的文件修改e.hasActivated="true"==e.hasActivated为e.hasActivated="true"=="true"定位到,在此标签前插入<script>window.οnlοad=function(){setTimeout(()=>{window.close();}......
  • Day30-方法的分类和调用
    Day30-方法的分类和调用方法的分类://静态方法有static//非静态方法没static方法的调用:​ 静态方法的调用:语法:​ 同一包中的不同类调用静态方法:​ 类名.方法名ClassB.staticMethod()​ 通过类名直接调用packagecom.example;classCla......
  • C#各种加密方法,字典排序
    1、字符串加密 ///<summary>///获取MD5加密字符串(type:0大写,1小写)///</summary>///<paramname="content">加密内容</param>///<paramname="type">返回格式(0大写,1小写)</param>///<returns></returns>pub......
  • Apache 配置出错常见问题及解决方法
    Apache配置出错常见问题及解决方法一、端口被占用问题问题描述:在启动Apache时,出现“Addressalreadyinuse”或类似的错误提示,这意味着Apache想要使用的端口已经被其他程序占用,导致Apache无法正常启动。原因分析:系统中已经有其他的应用程序在使用Apache......