SQL做题日志3||考试分数

1,320 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

🔖文章摘要

题目选自牛客网在线编程SQL实战,文章包含一组题,题目难度依次加深,内容依次为:①题目介绍、②代码编写要求、③解题思路、④解题代码和⑤要点总结共五大部分。

📝题目一(难度:简单)

📌题目介绍

牛客每次考试完,都会有一个成绩表(grade),如下:(解释:第1行表示用户id为1的用户选择了C++岗位并且考了11001分......第8行表示用户id为8的用户选择了JS岗位并且考了9999分)

📌编写要求

请写一个sql语句查询各个岗位分数的平均数,并且按照分数降序排序,结果保留小数点后面3位(3位之后四舍五入):

📌解题思路

📌解题代码

select job,round(avg(score),3)
from grade
group by job
order by score desc;

📌要点总结

  • 此题较为简单,主要考查聚合函数的使用以及排序

📝题目二(难度:中等)

📌题目介绍

牛客每次考试完,都会有一个成绩表(grade),如下:(解释:第1行表示用户id为1的用户选择了C++岗位并且考了11001分......第8行表示用户id为8的用户选择了前端岗位并且考了9999分)

 

📌编写要求

请写一个sql语句查询用户分数大于其所在工作(job)分数的平均分的所有grade的属性,并且以id的升序排序,如下:

📌解题思路

📌解题代码

select grade.*
from grade
left join (
    select job,avg(score) as ag
    from grade
    group by job
) as agt on grade.job=agt.job
where score>agt.ag
order by id;

📌要点总结

  • 该题在第一道题的基础上多考查了多表连接的使用

📝题目三(难度:中等)

📌题目介绍

牛客每次举办企业笔试的时候,企业一般都会有不同的语言岗位,比如C++工程师,JAVA工程师,Python工程师,每个用户笔试完有不同的分数,现在有一个分数(grade)表和不同的语言岗位(language)表简化如下:(解释:第1行表示用户id为1的选择了language_id为1岗位的最后考试完的分数为12000......第7行表示用户id为7的选择了language_id为2岗位的最后考试完的分数为11000)

image.png

📌编写要求

请找出每个岗位分数排名前2名的用户,得到的结果先按照language的name升序排序,再按照积分降序排序,最后按照grade的id升序排序,得到结果如下:

📌解题思路

📌解题代码

select table1.id,name,score
from (
    select *, 
    dense_rank() over(partition by language_id order by score desc) as rak
    from grade
) as table1
left join language on language.id=table1.language_id
where rak=1 or rak=2
order by name,score desc,table1.id;

📌要点总结

  • 这道题主要考查分组+排序,这种情况下使用dense_rank()over(partition by字段 order by 字段)比较容易解决,此外rank()也可以代替dense_rank(),只不过在这道题中使用起来不是很方便

  • 此题的另一解法:不使用排序函数,使用select自联表格本身来获取当前分数在当前岗位的排名

    select g1.id, l.name, g1.score
    from grade as g1 join language as l on g1.language_id=l.id 
    where 
    (
        select count(distinct g2.score) 
        from grade as g2 
        where g2.score>=g1.score and g1.language_id=g2.language_id
    ) <=2 
    order by l.name,g1.score desc ,g1.id;
    

📝题目四(难度:较难)

📌题目介绍

牛客每次考试完,都会有一个成绩表(grade),如下:(解释:第1行表示用户id为1的用户选择了C++岗位并且考了11001分......第8行表示用户id为8的用户选择了B语言岗位并且考了9999分)

📌编写要求

请写一个sql语句查询各个岗位分数升序排列之后的中位数位置的范围,并且按job升序排序,结果如下:

(解释:第1行表示C++岗位的中位数位置范围为[2,2],也就是2。因为C++岗位总共3个人,是奇数,所以中位数位置为2是正确的(即位置为2的10000是中位数)

第2行表示Java岗位的中位数位置范围为[1,2]。因为Java岗位总共2个人,是偶数,所以要知道中位数,需要知道2个位置的数字,而因为只有2个人,所以中位数位置为[1,2]是正确的(即需要知道位置为1的12000与位置为2的13000才能计算出中位数为12500)

第3行表示前端岗位的中位数位置范围为[2,2],也就是2。因为B语言岗位总共3个人,是奇数,所以中位数位置为2是正确的(即位置为2的11000是中位数)

📌解题思路

📌解题代码

select job,
floor((count(*)+1)/2 ) as "start",
ceil((count(*)+1)/2) as "end"
from grade
group by job
order by job;

📌要点总结

  • 这道题实际上解题思路是比较简单的,但容易被编写要求误导,导致想得很复杂,因此不要一味地依照编写要求去思考解题方案

  • floor()是向下取整函数,ceil()是向上取整函数

  • 该题的另一解法:向下取整函数floor()和向上取整函数ceil()的作用实际上可以用round()四舍五入函数替代!

    select grade.job, round(count(grade.id)/2), round((count(grade.id)+1)/2)
    from grade 
    group by grade.job
    order by job
    

📝题目五(难度:困难)

📌题目介绍

牛客每次考试完,都会有一个成绩表(grade),如下:(解释:第1行表示用户id为1的用户选择了C++岗位并且考了11001分......第8行表示用户id为8的用户选择了B语言岗位并且考了9999分)

📌编写要求

请写一个sql语句查询各个岗位分数的中位数位置上的所有grade信息,并且按id升序排序,结果如下:

(解释:第1行表示C++岗位的中位数位置上的为用户id为2,分数为10000,在C++岗位里面排名是第2

第2,3行表示Java岗位的中位数位置上的为用户id为4,5,分数为12000,13000,在Java岗位里面排名是第2,1

第4行表示B语言岗位的中位数位置上的为用户id为7,分数为11000,在前端岗位里面排名是第2)

📌解题思路

📌解题代码

select id,rak.job,score,t_rank
from (
    select *,row_number()over(partition by grade.job order by score desc) as t_rank
    from grade) as rak
left join (
    select job,ceil((count(*)+1)/2) as 'start',floor((count(*)+1)/2) as 'end'
    from grade
    group by job
) as mid on rak.job=mid.job
where t_rank=start or t_rank=end
order by id;

📌要点总结

  • 这道题在上一道题的解题铺垫下难度大大降低,主要考查row_number()函数的巧妙使用

  • 该题的另一解法:无论奇偶,中位数的位置距离(个数+1)/2小于1, 由上一道题的解题思路里的表格可以验证,解题代码如下

    select id,job,score,s_rank
    from 
    (select *
            ,(row_number()over(partition by job order by score desc))as s_rank
    	    ,(count(score)over(partition by job))as num
            from grade) as table1
    where abs(table1.s_rank-(table1.num+1)/2)<1
    order by id;