当前位置: 首页 > news >正文

详解力扣高频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)

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)

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
http://www.dtcms.com/a/299001.html

相关文章:

  • qt c++ msvc2017编译器解决界面中中文乱码问题
  • 数据赋能(336)——技术平台——智能化运营
  • 动态SQL标签
  • AI-调查研究-39-多模态大模型量化 微调与量化如何协同最大化性能与效率?
  • opencv学习(图像梯度)
  • 像素、视野、光源,都有哪些因素影响测量精度?
  • 【数据结构】栈和队列的实现
  • 【Java EE初阶 --- 网络原理】传输层---UDP/TCP协议
  • Spring boot Grafana优秀的监控模板
  • C++:list(2)list的模拟实现
  • 高并发微服务限流算法方案对比与实践指南
  • 【音视频协议篇】WebRTC 快速入门
  • 嵌入式——C语言:指针②
  • Kotlin线程同步
  • TCP/IP 网络编程面试题及解答
  • 数智管理学(三十九)
  • train_model_components ——组件模型的灵感,灵感
  • PYTHON从入门到实践-15数据可视化
  • 震网(Stuxnet):打开潘多拉魔盒的数字幽灵
  • Android ADB命令之内存统计与分析
  • C++对象模型
  • CH341 Linux驱动 没有 /dev/ttyCH341USB0
  • 前端模块化开发实战指南
  • kafka中生产者的数据分发策略
  • starrocks官网docker部署mysql无法连接
  • 影刀RPA_Temu关键词取数_源码解读
  • RK3568笔记九十三:基于RKNN Lite的YOLOv5目标检测
  • 高性能网络DPDK、RDMA、XDP初探
  • VTK交互——ClientData
  • Java程序员学从0学AI(六)