详解力扣高频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
相关推荐
一匹电信狗23 分钟前
【C++】异常详解(万字解读)
服务器·c++·算法·leetcode·小程序·stl·visual studio
柏油30 分钟前
MySQL 字符集 utf8 与 utf8mb4
数据库·后端·mysql
我科绝伦(Huanhuan Zhou)39 分钟前
异构数据库兼容力测评:KingbaseES 与 MySQL 的语法・功能・性能全场景验证解析
数据库·mysql
Apple_羊先森41 分钟前
Oracle数据库操作深入研究:备份、数据删除与性能优化
数据库·oracle·性能优化
BTU_YC44 分钟前
docker compose部署mysql
mysql·adb·docker
墨染点香1 小时前
LeetCode 刷题【43. 字符串相乘】
算法·leetcode·职场和发展
Keying,,,,2 小时前
力扣hot100 | 矩阵 | 73. 矩阵置零、54. 螺旋矩阵、48. 旋转图像、240. 搜索二维矩阵 II
python·算法·leetcode·矩阵
一宿君2 小时前
Github 9 个惊艳的开源 NL2SQL 项目
sql·nlp·github
麦麦大数据3 小时前
F004 新闻可视化系统爬虫更新数据+ flask + mysql架构
爬虫·mysql·flask·可视化·新闻
_不会dp不改名_3 小时前
leetcode_42 接雨水
算法·leetcode·职场和发展