由于下列题目是系列题由简单到难,所以我把它们按原来顺序,全写下来了
成绩表(grade),如下:
第1行表示用户id为1的用户选择了C++岗位并且考了11001分
。。。。。。
第8行表示用户id为8的用户选择了JS岗位并且考了9999分
查询各个岗位分数的平均数,并且按照分数降序排序,结果保留小数点后面3位(3位之后四舍五入):
select job,round(avg(score),3) avg
from grade
group by job
order by avg desc
分数表同上,查询用户分数大于其所在工作(job)分数的平均分的所有grade的属性,并且以id的升序排序,如下:
SELECT a.id,a.job,a.score
FROM (SELECT *,avg(score)over(Partition by job ) as avg from grade) a
where score > avg
企业笔试的时候,企业一般都会有不同的语言岗位,比如C++工程师,JAVA工程师,Python工程师,每个用户笔试完有不同的分数,现在有一个分数(grade)表简化如下:
不同的语言岗位(language)表简化如下:
请你找出每个岗位分数排名前2名的用户,得到的结果先按照language的name升序排序,再按照积分降序排序,最后按照grade的id升序排序,得到结果如下:
SELECT g.id,l.name,g.score
from(SELECT *,DENSE_RANK()over(partition by language_id order by score desc) as rn
from grade) g
join language l
on g.language_id = l.id
where rn <= 2
order by l.name,g.score desc,g.id
由于是分数排名前两位,所以用连续排名dense_rank()
牛客每次考试完,都会有一个成绩表(grade),如下:
查询各个岗位分数升序排列之后的中位数位置的范围,并且按job升序排序,结果如下:
有两种解法
第一种:分情况讨论,分为奇数时,偶数时
SELECT job
,case when count(score)%2=0 then ceiling(count(score)/2)
else ceiling(count(score)/2)
end as start1
,case when count(score)%2=0 then ceiling(count(score)/2+1)
else ceiling(count(score)/2)
end as end1
FROM grade
GROUP BY job
ORDER BY job;
第二种:大神解法
SELECT job,
floor(( count(*) + 1 )/ 2 ) AS "start",
floor(( count(*) + 2 )/ 2 ) AS 'end'
FROM grade
GROUP BY job
ORDER BY job
成绩表(grade),如下:
查询各个岗位分数的中位数位置上的所有grade信息,并且按id升序排序,结果如下:
两种解法
第一种:
SELECT a.* from (
SELECT *,ROW_NUMBER()over(partition by job order by score desc) as t_rank
FROM grade) a
WHERE (a.job,a.t_rank) in (
SELECT g1.job,
floor(( count(*) + 1 )/ 2 ) AS 'rank'
FROM grade g1
GROUP BY job
UNION
SELECT g2.job,
floor(( count(*) + 2 )/ 2 ) AS 'rank'
FROM grade g2
GROUP BY job)
ORDER BY id
第二种:大神解法,|(排名-总数/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)t1
where abs(t1.s_rank-(t1.num+1)/2)<1
order by id;
订单信息表(order_info),简况如下:
查询在2025-10-15以后状态为购买成功的C++课程或者Java课程或者Python的订单,并且按照order_info的id升序排序,以上例子查询结果如下:
SELECT * from order_info
where date > '2025-10-15' and status ='completed'
and product_name in ('C++','Python','Java')
order by id
订单信息表(order_info),简况如下:
查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的user_id,并且按照user_id升序排序,以上例子查询结果如下:
SELECT user_id from order_info
where date > '2025-10-15' and status ='completed'
and product_name in ('C++','Python','Java')
group by user_id
having count(1) >=2
order by user_id
订单信息表(order_info),简况如下:
查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的订单信息,并且按照order_info的id升序排序,以上例子查询结果如下:
方法一:用上一题找到的user_id 加上其他条件
select * from order_info
where user_id in(
SELECT user_id from order_info
where date > '2025-10-15' and status ='completed'
and product_name in ('C++','Python','Java')
group by user_id
having count(1) >=2)
and date > '2025-10-15' and status ='completed'
and product_name in ('C++','Python','Java')
方法二:用窗口函数
SELECT a.id,a.user_id,a.product_name,a.status,a.client_id,a.date
from(
select *,count(id)over(partition by user_id) as c
from order_info
where date > '2025-10-15'
and status ='completed'
and product_name in ('C++','Python','Java')
) a
where c>=2
order by a.id
订单信息表(order_info),简况如下:
查询在2025-10-15以后,如果有一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程,那么输出这个用户的user_id,以及满足前面条件的第一次购买成功的C++课程或Java课程或Python课程的日期first_buy_date,以及购买成功的C++课程或Java课程或Python课程的次数cnt,并且输出结果按照user_id升序排序,以上例子查询结果如下:
select a.user_id,min(date) first_buy_date,a.cnt
from(
select *,count(id)over(partition by user_id) as cnt
from order_info
where date > '2025-10-15'
and status ='completed'
and product_name in ('C++','Python','Java')) a
where a.cnt >= 2
group by a.user_id
order by a.user_id
订单信息表(order_info),简况如下:
查询在2025-10-15以后,如果有一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程,那么输出这个用户的user_id,以及满足前面条件的第一次购买成功的C++课程或Java课程或Python课程的日期first_buy_date,以及满足前面条件的第二次购买成功的C++课程或Java课程或Python课程的日期second_buy_date,以及购买成功的C++课程或Java课程或Python课程的次数cnt,并且输出结果按照user_id升序排序,以上例子查询结果如下:
select a.user_id, min(a.date) as first_buy_date,
max(a.date) as second_buy_date, a.cnt
from(select user_id,date,
row_number() over(partition by user_id order by date) as rank_no,
count(*) over(partition by user_id) as cnt
from order_info
where date>='2025-10-16'
and status='completed'
and product_name in('C++','Java','Python')
) a
where a.rank_no<=2 and a.cnt>=2
group by a.user_id
order by a.user_id
有一个订单信息表(order_info),简况如下:
有一个客户端表(client),简况如下:
查询在2025-10-15以后,同一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程的来源信息,第一列是显示的是客户端名字,如果是拼团订单则显示GroupBuy,第二列显示这个客户端(或者是拼团订单)有多少订单,最后结果按照第一列(source)升序排序,以上例子查询结果如下:
SELECT (case when b.is_group_buy ='No' then c.name else 'GroupBuy' end) source,
count(1)
from(
select *
from(
select *,count(oi.id)over(partition by user_id) as cnt
from order_info oi
where date > '2025-10-15'
and status ='completed'
and product_name in ('C++','Python','Java')
) a
where a.cnt>=2) b
left join client c
on c.id = b.client_id
group by source
order by source
简历信息表(resume_info),部分信息简况如下:
查询在2025年内投递简历的岗位和数量,并且按数量降序排序,以上例子查询结果如下:
select job,sum(num) cnt
from resume_info
where date like '2025%'
group by job
order by cnt desc
简历信息表(resume_info),部分信息简况如下:
查询在2025年内投递简历的每个岗位,每一个月内收到简历的数量,并且按先按月份降序排序,再按简历数目降序排序,以上例子查询结果如下:
方法一
select job,left(date,7) mon,sum(num) cnt
from resume_info
where date like'2025%'
group by job,mon
order by mon desc,cnt desc
方法二:
日期函数格式转换
DATE_FORMAT(date,format)
DATE_FORMAT() 函数用于以不同的格式显示日期/时间数据。
select job,DATE_FORMAT(date,"%Y-%m") mon,sum(num) cnt
from resume_info
where date like'2025%'
group by job,mon
order by mon desc,cnt desc
简历信息表(resume_info),部分信息简况如下:
查询在2025年投递简历的每个岗位,每一个月内收到简历的数目,和对应的2026年的同一个月同岗位,收到简历的数目,最后的结果先按first_year_mon月份降序,再按job降序排序显示,以上例子查询结果如下:
SELECT h1.job,first_year_mon,first_year_cnt,second_year_mon,second_year_cnt
FROM
-- 2025年的
(SELECT job,DATE_FORMAT(DATE,'%Y-%m') AS first_year_mon,
SUM(num) AS first_year_cnt
FROM resume_info
WHERE DATE LIKE '2025%'
GROUP BY job,first_year_mon) AS h1
JOIN
-- 2026年的
(SELECT job,DATE_FORMAT(DATE,'%Y-%m') AS second_year_mon,
SUM(num) AS second_year_cnt
FROM resume_info
WHERE DATE LIKE '2026%' -- 符合最左前缀匹配原则,也走索引
GROUP BY job,second_year_mon) AS h2
-- 表连接条件:两表job相同且月份相同,
-- DATE_FORMAT()变成字符串,使用right()函数取后两位即为月数
ON h1.job=h2.job AND right(first_year_mon,2)=right(second_year_mon,2)
ORDER BY first_year_mon DESC,h1.job DESC;
每个人的综合成绩用A,B,C,D,E表示,90分以上都是A,8090分都是B,6070分为C,50~60为D,E为50分以下
假设每个名次最多1个人,比如有2个A,那么必定有1个A是第1名,有1个A是第2名(综合成绩同分也会按照某一门的成绩分先后)。
每次SQL考试完之后,老师会将班级成绩表展示给同学看。
现在有班级成绩表(class_grade)如下:
查询,如果一个学生知道了自己综合成绩以后,最差是排第几名? 结果按照grade升序排序,以上例子查询如下:
select grade,sum(number)over(order by grade)
from class_grade
group by grade
每个人的综合成绩用A,B,C,D,E表示,90分以上都是A,8090分都是B,6070分为C,50~60为D,E为50分以下
假设每个名次最多1个人,比如有2个A,那么必定有1个A是第1名,有1个A是第2名(综合成绩同分也会按照某一门的成绩分先后)。
每次SQL考试完之后,老师会将班级成绩表展示给同学看。
现在有班级成绩表(class_grade)如下:
老师想知道学生们综合成绩的中位数是什么档位,请你写SQL帮忙查询一下,如果只有1个中位数,输出1个,如果有2个中位数,按grade升序输出,以上例子查询结果如下:
某一数的正序和逆序累计均大于整个序列的数字个数的一半即为中位数
select grade
from
(select grade,
(select sum(number) from class_grade) as total,
sum(number)over(order by grade) a, -- 求正序
sum(number)over(order by grade desc) b -- 求逆序
from class_grade
order by grade)t
where a >= total/2 and b >= total/2 -- 正序逆序均大于整个数列数字个数的一半
order by grade;
牛客每天有很多用户刷题,发帖,点赞,点踩等等,这些都会记录相应的积分。
有一个用户表(user),简况如下:
还有一个积分表(grade_info),简况如下:
查找积分增加最高的用户的名字,以及他的总积分是多少(此题数据保证积分最高的用户有且只有1个),以上例子查询结果如下:
select u.name,
sum(case when type = 'add' then grade_num else -(grade_num) end) grade_sum
from grade_info g
join user u
on u.id = g.user_id
group by u.name
order by grade_sum desc
limit 1
牛客每天有很多用户刷题,发帖,点赞,点踩等等,这些都会记录相应的积分。
有一个用户表(user),简况如下:
还有一个积分表(grade_info),简况如下:
查找积分增加最高的用户的id(可能有多个),名字,以及他的总积分是多少,查询结果按照id升序排序,以上例子查询结果如下:
select u.id,u.name,grade_sum
from(
#根据总积分排序
select a.*,rank()over(order by grade_sum desc) r
from(
#算出每个人的总积分
select g.user_id,
sum(case when type = 'add' then grade_num else -(grade_num) end) grade_sum
from grade_info g
group by user_id) a) b
join user u
on u.id = b.user_id
where r = 1
order by u.id