首页 > 试题广场 >

最差是第几名(二)

[编程题]最差是第几名(二)
  • 热度指数:60095 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
TM小哥和FH小妹在牛客大学若干年后成立了牛客SQL班,班的每个人的综合成绩用A,B,C,D,E表示,90分以上都是A,80~90分都是B,70~80分为C,60~70为D,E为60分以下
假设每个名次最多1个人,比如有2个A,那么必定有1个A是第1名,有1个A是第2名(综合成绩同分也会按照某一门的成绩分先后)。
每次SQL考试完之后,老师会将班级成绩表展示给同学看。
现在有班级成绩表(class_grade)如下:
grade number
A 2
C 4
B 4
D 2
第1行表示成绩为A的学生有2个
.......
最后1行表示成绩为D的学生有2个

老师想知道学生们综合成绩的中位数是什么档位,请你写SQL帮忙查询一下,如果只有1个中位数,输出1个,如果有2个中位数,按grade升序输出,以上例子查询结果如下:
grade
B
C
解析:
总体学生成绩排序如下:A, A, B, B, B, B, C, C, C, C, D, D,总共12个数,取中间的2个,取6,7为:B,C


示例1

输入

drop table if exists class_grade;
CREATE TABLE class_grade (
grade varchar(32) NOT NULL,
number int(4) NOT NULL
);

INSERT INTO class_grade VALUES
('A',2),
('C',4),
('B',4),
('D',2);

输出

B
C
with t1 as (
    select grade, sum(number) over(order by grade) as t_rank
    from class_grade
),
t2 as (
    select grade, sum(number) over(order by grade desc) as t_rank
    from class_grade
),
t3 as (select sum(number) cnt from class_grade),

t4 as(
    select grade from t1 
    corss join t3
    where t_rank >= (floor(cnt/2)+if(cnt&1,1,0)) 
),
t5 as(
    select grade from t2 
    corss join t3
    where t_rank >= (floor(cnt/2)+if(cnt&1,1,0)) 
)

select t4.grade from t4 join t5 on t4.grade = t5.grade; -- 正序、逆序 一夹

发表于 2025-01-31 12:27:24 回复(0)
参考大佬做法,求解中位数
select
grade
from(
select grade,
`number`,
(select sum(number) from class_grade) as total,
sum(`number`)over(order by grade asc) as counting_asc,
sum(`number`)over(order by grade desc) as counting_desc
from class_grade
)as t1
where counting_asc >= 0.5*total
and counting_desc >= 0.5*total
order by grade asc;


发表于 2024-11-09 15:24:51 回复(0)

哈哈,一个笨方法吧,中位数最多有两个,那么久直接把两个都找出来,然后去重就好了,注意排序哦

select *
from (
    select grade 
    from (
        select grade, sum(number) over(order by grade) as rn
        from class_grade
    ) t
    where rn >= (
        select floor((sum(number)+1)/2)
        from class_grade
    )
    order by grade asc 
    limit 1
) t
union 
select *
from (
    select grade 
    from (
        select grade, sum(number) over(order by grade) as rn
        from class_grade
    ) t
    where rn >= (
        select ceil((sum(number)+1)/2)
        from class_grade
    )
    order by grade asc 
    limit 1
) t
发表于 2024-08-14 18:08:17 回复(0)
正序位次逆序位次均大于等于总数一半的是中位数
select grade
from
(
select grade, 
(select sum(number) from class_grade) as t_num,
sum(number) over(order by grade asc) as rank_asc,
sum(number) over(order by grade desc) as rank_desc
from class_grade
order by grade
) t1
where rank_asc>=(t_num/2) and rank_desc>=(t_num/2);


发表于 2024-07-29 23:38:06 回复(1)
1.先计算各等级成绩的排名区间
startnum 为起始数,endnum为最后数
select grade,
sum(number) over(order by grade)-number+1 as startnum,
sum(number) over(order by grade) as endnum
from class_grade
得到结果
A
1
2
B
3
6
C
7
10
D
11
12
2.计算参与排名的总数,取中位数
select sum(number)/2 from class_grade
不管奇偶数,中位数都在该范围
floor((select sum(number)/2 from class_grade)+0.5)
ceiling((select sum(number)/2 from class_grade)+0.5)
合并后代码为:
select grade
from  
(select grade,
sum(number) over(order by grade)-number+1 as startnum,
sum(number) over(order by grade) as endnum
from class_grade) c
where floor((select sum(number)/2 from class_grade)+0.5)
between startnum and endnum
or ceiling((select sum(number)/2 from class_grade)+0.5)
between startnum and endnum






发表于 2024-05-19 19:49:18 回复(0)
不使用窗口函数
select cg.grade
from class_grade cg
where (
    select sum(`number`)
    from class_grade
    where grade < cg.grade
) <=(
    select sum(`number`) / 2
    from class_grade
) -- 中位数大于等级左边界
and (
    select sum(`number`)
    from class_grade
    where grade <= cg.grade
) >= (
    select sum(`number`) / 2
    from class_grade
) -- 中位数小于等级右边界
order by cg.grade
编辑于 2023-12-09 20:43:20 回复(0)
select grade from
(select grade
,(sum(number)over(order by grade)-number)+1 q1  #确定每个等级的最高名次
,sum(number)over(order by grade) q2  #确定每个等级的最低名次
,sum(number)over() zs  #顺便在同个表格确定好总人数(避免用GROUP BY)
from class_grade) x1
where floor((zs+1)/2) between q1 and q2 or floor((zs+2)/2) between q1 and q2 #确定好中位数并且进行筛选
order by grade
发表于 2023-11-25 15:42:39 回复(0)
WITH A AS(SELECT
*,
sum(number) OVER (ORDER BY grade) t
FROM class_grade)
SELECT
DISTINCT
grade
FROM A
JOIN (SELECT
floor((sum(number)+1)/2) mid
FROM class_grade
UNION
SELECT
floor((sum(number)+2)/2) mid
FROM class_grade) B
ON t>=B.mid
WHERE number+mid>t
ORDER BY grade

发表于 2023-10-19 14:40:45 回复(0)
求出每个成绩的最小位次和最大位次,分别命名为start,end,如果中位数在这两者之间,则选择对应的成绩grade
select grade from (
    select grade, 
    sum(number)over(order by grade) - number + 1 start,
    sum(number)over(order by grade) end
    from class_grade    
) t1
where floor(((select sum(number) from class_grade) + 1)/2) between t1.start and t1.end&nbs***bsp;floor(((select sum(number) from class_grade) + 2)/2) between t1.start and t1.end
order by grade

发表于 2023-06-30 18:59:14 回复(0)
n=sum(number)是偶数,则取第n/2和n/2+1个;否则取第n/2+0.5个
with t1 as ( # small和big之间范围 即 获得该grade的名次
    select *, (lag(big,1,0) over())+1 small
    from (
        select grade, sum(number) over (order by grade) big
        from class_grade ) a
), t2 as ( # 取第几个值?
    select round(if(n%2=0, n/2, n/2+0.5),0) v1, 
        round(if(n%2=0, n/2+1, n/2+0.5),0) v2 
    from ( select sum(number) n from class_grade ) b
)

select grade from t1 join t2 
on t2.v1 between t1.small and t1.big
UNION
select grade from t1 join t2 
on t2.v2 between t1.small and t1.big


发表于 2023-05-25 16:04:36 回复(0)
# 新建2列,总人数,前缀和
with t1 as (
    select *,
    sum(number) over(order by grade asc) as cumsum,
    sum(number) over() as total_num
    from class_grade
)

# 如果该grade【最好位次和最差位次区间】包括了【人数/2】
SELECT
  grade
FROM t1
WHERE cumsum - number <= total_num / 2 # 注意这里只能这么写,其他3种<(=)   >(=)  的组合不管用
AND cumsum >= total_num / 2;

发表于 2023-03-24 09:20:01 回复(0)
select grade
from (
    select grade,
    sum(number) over() as total,
    sum(number) over(order by grade) as total1,
    sum(number) over(order by grade desc) as total2
    from class_grade
)t 
where t.total1>=total/2 and t.total2 >= total/2
order by grade;

发表于 2023-01-10 16:56:35 回复(0)
#最直观解法,扩表直接查询
with recursive r(grade, number) as(
    select grade,number from class_grade
    union
    select grade,number-1 as number from r where number >= 2
)
select distinct b.grade
from (select sum(number)/2 m from class_grade) a,
(select *, row_number() over(order by grade,number) rk from r) b
where b.rk=m
or b.rk=m+0.5
or b.rk=m+1

发表于 2023-01-05 00:42:18 回复(0)
方法正逆向数为总数一半的数为中位数,总数为奇数时中位数只有一个,为偶数时中位数有两个!
select grade from (
    select grade,
    sum(number) over(order by grade asc) as s1,
    sum(number) over(order by grade desc) as s2,
    (select sum(number) from class_grade) total 
    from class_grade
)t
where s1>=total/2 and s2>=total/2
order by grade;

发表于 2022-11-17 09:18:00 回复(0)
求一位好心人解答,以下答案为什么错呀?
select grade
from (select grade, number
      , sum(number)over(order by grade) n1
      , sum(number)over(order by grade)-number n2
      , sum(number)over() cnt
      from class_grade) a
where (n1=round(cnt/2)&nbs***bsp;n1=round((cnt+1)/2)-1)
or (n2<round(cnt/2) and n1>=round((cnt+1)/2))


发表于 2022-08-12 11:15:01 回复(0)

来评论区学习

自己用两层view + 窗口函数lead()构造了左右边界,后面参考评论区各位大佬的3种解法,属实涨知识了。

分两类思想,一类构造左右边界 & 中值;一类问题转化,反向思考,当正序累计 & 逆序累计都大于中值时,则为中位数。

解1:两层view + lead()构造左右边界

with view4base as(
    select 
        grade,
        sum(number) over(order by grade) `right`,
        sum(number) over() total
    from class_grade
),view4lead as(
    select
        grade,total,
        lead(`right` + 1,1)  over(order by `right` desc) `left`,`right`
    from view4base  
)
select grade
from view4lead
where (total >> 1) + 1 >= `left` and (total >> 1) + 1 <= `right`
    or total & 1 = 0 and (total >> 1) >= `left` and (total>>1) <= `right`
order by grade

解2:直接做运算得到想要的值--左边界。

with view4base as(
    select 
        grade,
        sum(number) over(order by grade) - number `left`,
        sum(number) over(order by grade) + 1 `right`,
        sum(number) over() total
    from class_grade
)
select grade
from view4base
where (total >> 1) + 1 > `left` and (total >> 1) + 1 < `right`
    or total & 1 = 0 and (total >> 1) > `left` and (total >> 1) < `right`
order by grade

解3:中位数:n + 1 / 2 和 n / 2 + 1

with view4base as(
    select 
        grade,
        sum(number) over(order by grade) - number `left`,
        sum(number) over(order by grade) + 1 `right`,
        # 中位数:n + 1 / 2 和 n / 2 + 1
         sum(number) over() +  1 >> 1 mid1,
        (sum(number) over() >> 1) + 1 mid2
    from class_grade
)
select grade
from view4base
where mid1 > `left` and mid1 < `right` or mid2 > `left` and mid2 < `right`
order by grade

解4:当一个数正序 & 逆序累计都大于等于中值时(非取整,保留0.5),这个数为中位数。

with view4base as(
    select
        grade,
        sum(number) over(order by grade) m,
        sum(number) over(order by grade desc) n,
        sum(number) over() / 2  mid
    from class_grade
)
select grade
from view4base
where m >= mid and n >= mid
order by grade
发表于 2022-08-03 14:52:51 回复(0)