牛客网之考试分数(5)

一.题目概述: 牛客每次考试完,都会有一个成绩表(grade),如下:

第1行表示用户id为1的用户选择了C++岗位并且考了11001分

。。。

第8行表示用户id为8的用户选择了前端岗位并且考了9999分

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

解释:

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

(注意: sqlite 1/2得到的不是0.5,得到的是0,只有1*1.0/2才会得到0.5,sqlite四舍五入的函数为round,sqlite不支持floor函数,支持cast(x as integer) 函数,不支持if函数,支持case when …then …else …end函数,sqlite不支持自定义变量)

二.用例输入:

drop table if exists grade;
CREATE TABLE  grade(
`id` int(4) NOT NULL,
`job` varchar(32) NOT NULL,
`score` int(10) NOT NULL,
PRIMARY KEY (`id`));


INSERT INTO grade VALUES
(1,C++,11001),
(2,C++,10000),
(3,C++,9000),
(4,Java,12000),
(5,Java,13000),
(6,前端,12000),
(7,前端,11000),
(8,前端,9999);

三.分析: 中位数的概念运用到sql中时,应注意第1,2位置上的数据,如题即count(id) = 2时,中位数应为1,2,应把1,2位置上的数据全部展现出来。

四.思路: (1).由题意 ”查询各个岗位分数的中位数位置上的所有grade信息,并且按id升序排序“ (2).表1 求出各个岗位的id总数 ==> 求出岗位的中位数并注意考虑特殊值(即当count = 2时,(count(id)+1)/2 应再加1,保证两个数都能取到值 eg: mysql:FLOOR((COUNT()+1)/2)+if(COUNT() % 2=1,0,1) other: (cast((count(id)+1)/2 AS INTEGER)+(case when count(id)%2=1 then 0 else 1 end)) as ‘end’ ) (3).表2 保证id按score正确排序,联立表1即可求出score中位数

整合得

select B.* from
(select job, cast((count(id)+1)/2 AS INTEGER) as start , (cast((count(id)+1)/2 AS INTEGER)+(case when count(id)%2=1 then 0 else 1 end)) as end
from grade 
group by job) A
JOIN
(select g1.*, (
    select count(distinct g2.score) 
    from grade g2 
    where g2.score>=g1.score and g1.job=g2.job) as rank
from grade g1 ) B

on (A.job=B.job  and B.rank between A.start and A.end)
order by B.id

五.总结: sql执行顺序:from - where - group - select - order - limit

(select g1.*, (
    select count(distinct g2.score) 
    from grade g2 
    where g2.score>=g1.score and g1.job=g2.job) as rank
from grade g1 ) B

自写解析版

# 查询各个岗位分数的中位数位置上的所有grade信息,并且按id升序排序
# 每个分组按分数查询,按id升/降序

# 中位数
SELECT 
  tmp_by_job.id,tmp_by_job.job,tmp_by_job.score from
	# 按岗位分组 列出排序后的表状态
	(select g1.id as id ,g1.job as job,g1.score as score, (
    select count(distinct g2.score) 
    from grade g2 
    where g2.score>=g1.score and g1.job=g2.job) as t_rank
    from grade g1 ) as tmp_by_job
	INNER JOIN
	# 找出每个岗位的id数
	# (cast((count(id)+1)/2 AS INTEGER)+(case when count(id)%2=1 then 0 else 1 end))
	(SELECT id,job,FLOOR((COUNT(*)+1)/2) as mid ,FLOOR((COUNT(*)+1)/2)+if(COUNT(*) % 2=1,0,1) AS end FROM grade GROUP BY job) as count_by_job
	on (tmp_by_job.job=count_by_job.job  and tmp_by_job.t_rank between count_by_job.mid and count_by_job.end)
	order by tmp_by_job.id
经验分享 程序员 微信小程序 职场和发展