刷完了简单难度的数据库题目,对sql中的一些语法特性有了简单的了解,算是学会了挺多思路,还有很多的小技巧
总之就是感觉使用sql能完成的任务越来越多了,能解决的业务场景也也越来越复杂了。有许多之前不得不拿到程序中才能处理的数据使用简单的sql就能够达成,当事人就很有成就感。
所以刷题整理这个过程还会持续下去,从这篇博文开始,就是中等难度的了,由于题目较长或者题解较简单稍微复杂。所以为了避免每篇博文过于冗长,暂定是5-6道题一篇。会挑选博主觉得比较有意义的题目(根据博主不算高的水平)
往期的题解会放在最后,欢迎阅览、交流、讨论。
产品销售分析III
题目链接
这个题需要我们找出所有产品第一年的销售信息。
如果直接分组的话,由于分组后各字段的联系被切断不能同时拿到第一年的所有信息。
所以可以先筛选出来所有产品的第一年作为答案集合。再使用in按照产品id和年份两个关键字来匹配这集合,找出所有的产品第一年销售信息。
select
product_id,
year as first_year,
quantity,
price
from
sales
where
(product_id,year) in (
select
product_id,
min(year) as year
from
sales
group by
product_id
)
;
二级关注者
题目链接
查询所有的关注者二级关注者的数目,二级关注者是关注关注者的人,这个定义就逃不掉要进行自联结。
但是在进行自联结之前我们要将一级关注者进行去重,不然在分组后会产生重复。
去重后直接进行自联结就好,后表的followee是前表的follower。
之后对联结表按照前表的follower进行分组,分别统计每组的关注者数目即可
select
f1.follower,
count(distinct f2.follower) as num
from
(
select
distinct follower
from
follow
) f1
join follow f2 on f1.follower = f2.followee
group by
f1.follower
order by
f1.follower
;
换座位
题目链接
这道题需要我们交换相邻两个学生的位置。
由于id是从1开始计数的,那么相邻的学生的id就应该满足如下关系:
⌊ i d 1 + 1 2 ⌋ = ⌊ i d 2 + 1 2 ⌋ i d 1 ≠ i d 2 \left \lfloor \frac{id_1 + 1} {2} \right \rfloor = \left \lfloor \frac{id_2 + 1} {2} \right \rfloor \\ id_1 ≠ id_2 ⌊2id1+1⌋=⌊2id2+1⌋id1=id2
所以我们将原表单按照上面的规则进行自联结即可,最后答案就是后表的student字段
对于奇数最后剩下的那个学生,后表的student字段值为null,使用ifnull转而打印前表的student字段即可。
select
s1.id,
ifnull(s2.student,s1.student) student
from
seat s1
left join seat s2 on s1.id != s2.id and floor((s1.id + 1)/2) = floor((s2.id + 1)/2)
;
或者,我们也不难发现,对于奇数节点需要拿取下面的值,偶数节点需要拿取上面的值。所以可以自联结两次,分别将上面的元素和下面的元素联结在一起,最后的元素是奇数节点,没有下面的值,这个值依旧是null还是可以使用ifnull进行处理。
select
s1.id,
ifnull((
case
when s1.id % 2 = 0 then s2.student
else s3.student
end
),s1.student) as student
from
seat s1
left join seat s2 on s1.id = s2.id + 1
left join seat s3 on s1.id = s3.id - 1
;
项目员工III
题目链接
这道题需要我们报告所有的经验丰富雇员,这个所有是一个关键,这意味着我们大概率需要额外的使用子查询来先查出这个经验最丰富的的标准而非直接分组取最值。
所以我们就先区查询出所有项目组中经验最丰富的的雇员的经验值,并将它与工程表单相联结挑选出其中经验最丰富的员工。
select
distinct p.project_id,
e.employee_id
from
project p
join (
select
project_id,
max(experience_years) mexp
from
project p
left join employee e on p.employee_id = e.employee_id
group by
project_id
) d on p.project_id = d.project_id
join employee e on e.experience_years = d.mexp and p.employee_id = e.employee_id
;
每日新用户统计
题目链接
这个题有些绕需要查询每个日期所有首次登录的用户数量。这是个问题,意味着有一部分的日期信息在这次统计中是无效的,我们需要先去查询出所有用于首次登录的时间,这些日期是有效的。接着我们对这些日期进行分组,并统计其中学生的人数。
对于其中90天的限制添加在哪里也是个问题,它不能直接的放在筛选玩家首次登录的过程中。这样有些玩家首次登录时间早于90天前的记录会被忽略掉,所以最好在外面查询分组前筛选这个90天。
select
activity_date as login_date,
count(distinct user_id) as user_count
from
(
select
user_id,
min(activity_date) activity_date
from
traffic
where
activity = 'login'
group by
user_id
)g
where
datediff('2019-06-30',activity_date) <= 90
group by
activity_date
每位学生的最高成绩
题目链接
这道题依旧是取最值及其相关信息。
需要先用一个子查询来查询出最值,再按照这个最值在原表中进行筛选。
由于取最高成绩的科目可能会有很多科,为了获得其中每个学生id最小的得分最高的科目,需要对学生进行分组。分组后使用min就可以获得id最小的科目了。
select
student_id,
min(course_id) as course_id,
grade
from
enrollments
where
(student_id,grade)in(
select
student_id,
max(grade)
from
enrollments
group by
student_id
)
group by
student_id
order by
student_id