详解力扣高频SQL50题之550. 游戏玩法分析 IV【中等】

传送门:550. 游戏玩法分析 IV

题目

Table: Activity

±-------------±--------+

| Column Name | Type |

±-------------±--------+

| player_id | int |

| device_id | int |

| event_date | date |

| games_played | int |

±-------------±--------+

(player_id,event_date)是此表的主键(具有唯一值的列的组合)。

这张表显示了某些游戏的玩家的活动情况。

每一行是一个玩家的记录,他在某一天使用某个设备注销之前登录并玩了很多游戏(可能是 0)。

编写解决方案,报告在首次登录的第二天再次登录的玩家的 比率,四舍五入到小数点后两位。换句话说,你需要计算从首次登录后的第二天登录的玩家数量,并将其除以总玩家数。

结果格式如下所示:

示例 1:

输入:

Activity table:

±----------±----------±-----------±-------------+

| 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-02 | 0 |

| 3 | 4 | 2018-07-03 | 5 |

±----------±----------±-----------±-------------+

输出:

±----------+

| fraction |

±----------+

| 0.33 |

±----------+

解释:

只有 ID 为 1 的玩家在第一天登录后才重新登录,所以答案是 1/3 = 0.33

解析

这题对MySQL选手来说是中等难度,对Oracle选手却是困难 ,后面会讲原因。

题目说了第二天登录,所以要再关联一张表,该表的日期都是第一天的日期即最小日期,然后让原表的日期=最小日期+1。具体来说,首先要对玩家id分组,再用min()求出最小日期,再和其他字段作为一张表,再与原表连接,连接条件是原表.id=最小日期表id且原表的日期=最小日期+1,这样就筛出第二天登录的玩家信息了,然后玩家总数用子查询统计,子查询查询原表的不重复的玩家id数。最后,用count()除以子查询结果*100即所求。注意日期+1和聚合函数除以子查询在不同数据库实现不同,mysql的日期+1必须用dateadd()函数,允许聚合函数除以子查询,但oracle可直接用日期+1,不允许在未分组的情况下使用聚合函数除以子查询,接下来先给出mysql解法,再给出oracle解法。

算法(MySQL)

查询活动表,对玩家id分组,再用min()求出最小日期,用count()计算玩家数,再和其他字段作为一张表,再与原表连接,连接条件是原表的日期=最小日期+1,用dateadd()实现。连接完后,用count()统计连续登录两天的玩家数,用子查询统计玩家总数,其中子查询用count()+distinct统计原表的玩家总数,最后用连续登录两天的玩家数/玩家总数*100即连续登录率,返回该字段即可。

代码(MySQL)

sql 复制代码
select round(count(*)/
(select count(distinct player_id) from Activity),2)as fraction
from(select player_id,min(event_date) as first_date
from Activity
group by player_id)t
join Activity a
on a.player_id=t.player_id and a.event_date = DATE_ADD(t.first_date, INTERVAL 1 DAY);

以下内容MySQL选手可以不用看了,Oracle选手必看。

解析(Oracle)

核心算法前面讲过,不再赘述,这里讲Oracle实现。首先,直接用count()/(select count()...)会报错:ORA-00937: not a single-group group function,意思就是若不分组,非聚合字段是不允许和聚合函数一起使用的 ,也就是要么用group by,要么对字段聚合,这里group by未必要对子查询分组,对任何字段都可以,比如玩家id,但我们要求的是表中所有行数,所以不能用group by。所以只能对子查询使用聚合函数,满足要么都聚合,要么分组+聚合 的语法规则。聚合函数只要能保持原来的值就行,比如min(),max(),这里取max()。但是这还没结束 ,因为Oracle中外层查询和子查询是相关的,外层查询的来源表,即外层from后的表为空,则任何子查询为空,一定返回NULL,而MySQL子查询是和外层查询独立的,即便外层查询的来源表为空,也不影响子查询返回数据。所以当不存在连续登录两天的玩家时,在Oracle中就需要用NVL处理NULL值,要么对count()/max()的结果使用NVL,要么对round()使用NVL,至此,Oracle实现难点才算结束!

代码(Oracle)

sql 复制代码
select round(NVL(count(*)/
max((select count(distinct player_id) from Activity)),0),2)
as fraction
from(select player_id,min(event_date) as first_date
from Activity
group by player_id)t
join Activity a
on a.player_id=t.player_id and a.event_date=t.first_date+1
相关推荐
韩立学长20 小时前
【开题答辩实录分享】以《植物爱好者交流平台的设计与实现》为例进行答辩实录分享
spring boot·后端·mysql
笨手笨脚の21 小时前
Mysql 的锁机制
数据库·mysql··死锁·间隙锁
合作小小程序员小小店21 小时前
web网页开发,在线%商城,电商,商品购买%系统demo,基于vscode,apache,html,css,jquery,php,mysql数据库
开发语言·前端·数据库·mysql·html·php·电商
星光一影1 天前
基于SpringBoot智慧社区系统/乡村振兴系统/大数据与人工智能平台
大数据·spring boot·后端·mysql·elasticsearch·vue
夏鹏今天学习了吗1 天前
【LeetCode热题100(64/100)】搜索旋转排序数组
算法·leetcode·职场和发展
alphaTao1 天前
LeetCode 每日一题 2025/11/3-2025/11/9
windows·leetcode
懒羊羊不懒@1 天前
【MySQL | 基础】概念
数据库·mysql
laplace01231 天前
Java八股—MySQL
java·mysql·oracle
你想考研啊1 天前
oracle导出 导入
数据库·oracle
IT 小阿姨(数据库)1 天前
PostgreSQL 之上的开源时序数据库 TimescaleDB 详解
运维·数据库·sql·postgresql·开源·centos·时序数据库