详解力扣高频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
相关推荐
自信的小螺丝钉1 天前
Leetcode 240. 搜索二维矩阵 II 矩阵 / 二分
算法·leetcode·矩阵
li35741 天前
从“内存操作”到“原子更新”:一次代码思维的跃迁
数据库·oracle
瀚高PG实验室1 天前
执行select * from a where rownum<1;,数据库子进程崩溃,业务中断。
数据库·sql·瀚高数据库
小白考证进阶中1 天前
终于赶在考试券过期前把Oracle OCP证书考下来了!
运维·数据库·oracle·dba·开闭原则·数据库管理员
KING BOB!!!1 天前
Leetcode高频 SQL 50 题(基础版)题目记录
sql·mysql·算法·leetcode
川石课堂软件测试1 天前
Oracle 数据库如何查询列
linux·数据库·sql·功能测试·oracle·grafana·prometheus
一支鱼1 天前
leetcode-6-正则表达式匹配
算法·leetcode·typescript
NineData1 天前
NineData发布 Oracle 到 MySQL 双向实时复制,助力去 O 战略与数据回流
mysql·阿里云·oracle·ninedata·数据库迁移·数据复制·双向复制
川石课堂软件测试1 天前
Oracle 数据库使用事务确保数据的安全
数据库·python·功能测试·docker·oracle·单元测试·prometheus
皆过客,揽星河1 天前
mysql初学者练习题(从基础到进阶,相关数据sql脚本在最后)
数据库·sql·mysql·oracle·mysql基础练习·mysql基础语法·数据库练习题