刷完了简单难度的数据库题目,对sql中的一些语法特性有了简单的了解,算是学会了挺多思路,还有很多的小技巧
总之就是感觉使用sql能完成的任务越来越多了,能解决的业务场景也也越来越复杂了。有许多之前不得不拿到程序中才能处理的数据使用简单的sql就能够达成,当事人就很有成就感。
所以刷题整理这个过程还会持续下去,从这篇博文开始,就是中等难度的了,由于题目较长或者题解较简单稍微复杂。所以为了避免每篇博文过于冗长,暂定是5-6道题一篇。会挑选博主觉得比较有意义的题目(根据博主不算高的水平)
往期的题解会放在最后,欢迎阅览、交流、讨论。
题目链接
这道题询问我们第n高的薪水,其实逻辑很简单。主要是这道题使用到了存储过程,作为第一个在刷题过程中见到使用存储过程的题目,挺有纪念意义的。
第n个我们可以使用limit n,0
在排序后来找到,但需要注意的点就是我们的“第n个”在表中其实是“第n-1个”所以需要先使n减去一才行。
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
set N = N - 1;
RETURN (
# Write your MySQL query statement below.
select
salary as getNthHighestSalary
from
employee
group by
salary
order by
salary desc
limit N,1
);
END
题目链接
这个题目需要我么能找到连续出现三次的数字,这个套路有些像简单题中的连续空余座位。
对于连续的元素,我们比较同一列中每个元素的上下两行即可,如果一个元素同上下两个元素都相等,那么这个元素就连续出现了三次。
为了实现同一列不同行比较,我们可以采用自联结的方式使得不同行的数据联结在同一行。具体到这一题,我们就自联结两次,分别将一个元素的上面和下面元素联结到该元素后面,之后我们筛选出所有同上下元素相同的元素即可。
select
distinct l1.num as ConsecutiveNums
from
logs l1
join logs l2 on l1.id = l2.id - 1
join logs l3 on l1.id = l3.id + 1
where
l1.num = l2.num
and
l1.num = l3.num
;
题目链接
这道题,乍一看,挺简单的。不就是将所有员工按照部门分组,然后筛选出工资最大的那个员工嘛。
这么想是挺简单的,但问题在于,员工的名称和工资是两个字段,分组后相对是独立的。我们可以取出部门中最高的工资,但是没有办法找出领取这份工资对应的员工。这就是问题所在。
解决办法就是,使用子查询,既然只能找到每个部门最高工资,那我们就直接查询出所有部门的最高工资作为答案集合,然后将部门id和工资当做共同关键字对表单进行筛选。这样,所有符合部门工资最高的员工就会被筛选出来,最后记得连接部门表单加上部门的名称。
select
d.name as department,
e.name as employee,
salary
from
employee e
left join department d on e.departmentid = d.id
where
(d.id,salary) in(
select
departmentid,
max(salary)
from
employee
group by
departmentid
)
;
题目链接
这道题需要我们统计每个玩家在每个时间段玩过多少游戏,从这个描述中为我们大概可以得知,这组查询的关键字是两个,即玩家和登录日期
另外我们需要统计每个时间点前玩家玩过多少游戏,这需要我们使用自联结,将每个玩家的每个登录时间同他之前包括该次的登录给联结起来。值得注意的是,这里的联结条件仍然是两个关键字。
最后,我们将联结好的表按照玩家和登录日期分组并使用聚合函数统计其总共玩游戏的数目即可。
select
a1.player_id,
a1.event_date,
sum(a2.games_played) as games_played_so_far
from
activity a1
left join activity a2 on a1.event_date >= a2.event_date and a1.player_id = a2.player_id
group by
a1.event_date,a1.player_id
order by
a1.player_id,a1.event_date
对于所有玩家的数量,我们可以使用聚合函数直接查询出来。
对于那些存在首次登录第二天再次登录的玩家数量,我们可以使用子查询,返回单行单列,作为元素直接使用。
查询这些玩家还需要费一些功夫。一个最朴素的思路就是使用自联结将所有首次登录和次日登录的玩家联结起来。
那么就需要先查询出每个玩家的首次登录时间,这个可以使用子查询来完成。所以这道题可能需要使用到两层子查询进行嵌套。
最后不要忘记保留两位小数和null的情况。
select
round(ifnull((
select
count(distinct a1.player_id)
from
(
select
player_id,
min(event_date) event_date
from
activity
group by
player_id
) a1
join activity a2 on a1.player_id = a2.player_id and datediff(a1.event_date,a2.event_date) = -1
)/count(distinct player_id),0),2) as fraction
from
activity
;