其中日期 = '2022-08-13'

那么您选择从哪个表中进行过滤呢?

根据问题的意思:如果玩家没有登出日志,就会使用登录日志的等级信息,也就是说我们需要从“登出日志”两者中获取编号和“登录日志”。然后,我们可以将“注销日志”和“登录日志”两张表合并为一张表来使用。

如何连接?

首先我们要明白“停留在各个层次”的含义。根据题意结合“停留”一次的字面意思,我们可以知道某天的“停留在各个等级”是指该玩家当天最后一次退出游戏时的等级。

又因为玩家每次退出时,时间一定是在对应的登录时间之后,而玩家未退出时则使用登录日志的等级信息。因此,无论玩家是否登录或退出,当日每个玩家在游戏中的状态为 最后一次的等级为停留等级。

也就是说,我们可以将“登出日志”和“登录日志”垂直连接起来,然后通过连接的表按照登录或登出时间对每个玩家进行排序,从而一次性过滤掉每个玩家等级的最后一条日志。

“注销日志”和“登录日志”中字段的数量和含义是一致的,因此可以使用union all子句来垂直连接“注销日志”和“登录日志”。

另外,为了提高查询速度,我们可以在加入之前分别过滤“注销日志”和“登录日志”的日期。

如何编写垂直连接“注销日志”和“登录日志”的SQL:


#列出特定字段,并对字段名不一致的字段进行重命名,保证多表连接时字段对应正确。
选择日期、角色 ID、登录时间、角色级别
从登录日志
其中日期 = '2022-08-13'
联合所有
#列出特定字段,并对字段名不一致的字段进行重命名,保证多表连接时字段对应正确。
选择日期、角色 ID、注销时间、角色级别
从注销日志
其中日期 = '2022-08-13';

UNION 运算符用于组合两个或多个语句的结果集。

注意:UNION 内部的语句必须具有相同的列数。列也必须具有类似的数据类型。此外,每个语句中的列顺序必须相同。 Union 效率低下,因为它需要扫描重复值。如果合并不是故意删除重复行,则使用Union All。被连接的两条SQL语句的字段数量必须相同,并且字段类型必须“兼容”(一致)。

含义:如果我们需要将两个语句的结果作为一个整体显示,我们需要使用union或者union all关键字。 union(或union)的作用是将多个结果组合起来并显示在一起。

差异

union和union all的区别在于union会自动压缩多个结果集中的重复结果,而union all会显示所有结果,无论是否重复。

Union:两个结果集的并集,排除重复行,并按默认规则排序;

Union All:对两个结果集进行并集操作,包括重复行,不排序;

垂直连接后,根据连接的表(设为临时表a)按时间对每个玩家进行排序,并找出每个玩家最后一次的等级。

按时间对每个玩家进行排序就是分组排序,可以利用排序窗口功能来实现。即:按角色id分组(by character id),按时间排序(order by time),获取每个玩家在每个时间的排名。

为了方便过滤掉最后一次,我们对时间进行降序排序(order by time desc),因为降序排序时,最后一次的排名始终是1,所以我们直接过滤掉排名为1的数据,即最近一次的数据可用。

排序窗口函数共有三种:rank()、()、()。那么我们应该选择哪种排序窗函数呢?

rank()、()、()之间的区别是:

rank()函数:生成重复的不连续排名代码;

()函数:生成重复且连续的排序代码;

()函数:生成连续且不重复的排序代码。

我们的目的是获得上次的排名。由于是降序排列,无论使用哪种排序窗口函数,最后一次的排名始终是1。

所以这三个排序窗函数都可以用,选一个就可以了。在这里,我们选择rank()函数。

这样对每个玩家按时间排序的SQL语句就是:


select *,rank() over(partition by role id order by time desc) 作为排名
来自a;

其中,a是之前垂直连接的桌子。带进来后SQL的写法是:


select *,rank() over(partition by role id order by time desc) 作为排名
从
(选择日期、角色id、登录时间为时间、角色级别
从登录日志
其中日期 = '2022-08-13'
联合所有
选择日期、角色 ID、注销时间、角色级别
从注销日志
其中日期 = '2022-08-13') as a;

将上述查询结果设置到临时表b中,从临时表中筛选出排名第1(其中ranking=1)的数据,即可得到玩家的停留等级信息。

如何编写SQL:


选择 *
从 b
其中排名 = 1;

引入临时表b的具体SQL语句。完整的SQL写法:

选择 *
从
(select *,rank() over(partition by role id order by time desc) 作为排名
从
(选择日期、角色id、登录时间为时间、角色级别
从登录日志
其中日期 = '2022-08-13'
联合所有
选择日期、角色 ID、注销时间、角色级别
从注销日志
其中日期 = '2022-08-13') 作为
) 作为 b
其中排名 = 1;

最后,我们来计算一下每个级别停留的角色数量。

计算每一层停留的角色数量分为两步:

第一步是将停留的角色等级进行分组;

第二步,计算分组后每一级的字符数。

可以看到,这实际上是一个分组汇总问题,要计算数量,可以使用count()函数,一个带有计数功能的聚合函数。

因此,我们使用group by子句结合count()函数来计算每一层停留的字符数。

如何编写SQL:

选择角色等级作为停留等级,count(角色id)作为角色数量
从
(select *,rank() over(partition by role id order by time desc) 作为排名
从
(选择日期、角色id、登录时间为时间、角色级别
从登录日志
其中日期 = '2022-08-13'
联合所有
选择日期、角色 ID、注销时间、角色级别
从注销日志
其中日期 = '2022-08-13') 作为
) 作为 b
其中等级 = 1
按角色级别分组
按字符级别 asc 排序; #将角色等级从小到大排列,得到等级分布