问题描述:老师想知道学生们综合成绩的中位数是什么档位,请你写SQL帮忙查询一下,如果只有1个中位数,输出1个,如果有2个中位数,按grade升序输出,以上例子查询结果如下:
图片说明

方案1:设立三个表,计算中位数表t,人数累积表s,人数表(人数前移)l

WITH t AS (         # 中位数表
       SELECT CASE WHEN MOD(SUM(number),2) = 0 
               THEN SUM(number)/2 ELSE (SUM(number)+1)/2 
                  END AS start_num,
          CASE WHEN MOD(SUM(number),2) = 0
               THEN (SUM(number)+2)/2 ELSE (SUM(number)+1)/2                     
                 END AS end_num
          FROM class_grade
                    ) ,
     s AS (       # 人数累积表
       SELECT grade,SUM(number) OVER(ORDER BY grade ASC) AS sum_num
       FROM class_grade
       ORDER BY grade ASC
      ) ,
     l AS (     # 人数累积表的前移
      SELECT t.grade,IFNULL(LAG(t.sum_num) OVER(ORDER BY t.grade ASC),0) AS up_num
      FROM  (SELECT grade,SUM(number) OVER(ORDER BY grade ASC) AS sum_num

         FROM class_grade

         ORDER BY grade ASC) t
    )
SELECT l.grade
FROM l
INNER JOIN s
ON l.grade = s.grade
CROSS JOIN t
WHERE (t.start_num > l.up_num AND t.end_num <= s.sum_num)
     OR (t.start_num > l.up_num AND t.start_num <= s.sum_num) 
     OR (t.end_num > l.up_num AND t.end_num <= s.sum_num)
ORDER BY l.grade ASC;

思路讲解
中位数表t求解出中位数(两个,令其为start_num,end_num)
人数累计表s计算出每个grade对应的人数最大值,作为中位数对比的区间上界
l表计算出每个grade对应的人数最小值,作为中位数对比的区间下界
(不推荐,太繁琐了)

方案2:采用SunburstRun 的很妙的解法,将ABCD对应人数正序倒序一起排序,同时满足正序和倒序数都大于整个数列个数的一半的数即为中位数。
大佬说得很清楚了,详情可查看https://blog.nowcoder.net/n/caaa953ea61049fdab7cd6a303a6e8ad?f=comment
这里我再还原一下代码

SELECT grade
FROM (
      SELECT grade,(SELECT SUM(number) FROM class_grade ) as total,
         SUM(number) OVER(ORDER BY grade ASC) AS rank_1,
         SUM(number) OVER(ORDER BY grade DESC) AS rank_2
     FROM class_grade
     ) t
WHERE total/2 <= rank_1 AND total/2 <= rank_2
ORDER BY grade ASC;

方案3:采用多个子查询的嵌套
参考Karra
https://blog.nowcoder.net/n/f646857973ea4b48b8356c02f57830ad?f=comment

SELECT t2.grade
FROM (
      SELECT *,(t.sum_num-t.number) AS t_num,SUM(number) OVER() AS total
      FROM(SELECT *,SUM(number) OVER(ORDER BY grade ASC) AS sum_num
           FROM class_grade) t
      ) t2
WHERE t2.total/2 BETWEEN t2.t_num AND t2.sum_num