MySQL查询各科成绩前三名的记录与排名的思路分析(不考虑并列)

  • 表结构如下图
+-----+------------+-----------+-------+
| sid | student_id | course_id | score |
+-----+------------+-----------+-------+
|   1 |          1 |         1 |    60 |
|   2 |          1 |         2 |    59 |
|   3 |          2 |         2 |    99 |
|   5 |          3 |         1 |    77 |
|   6 |          3 |         2 |    78 |
|   7 |          4 |         1 |    59 |
|   8 |          5 |         2 |    20 |
|   9 |          6 |         1 |    99 |
|  10 |          6 |         2 |   100 |
|  11 |          7 |         1 |     0 |
|  12 |          7 |         2 |     1 |
|  13 |          8 |         1 |   100 |
|  14 |          9 |         2 |   100 |
|  15 |          9 |         3 |    50 |
|  16 |          9 |         1 |    60 |
  • 排名方法如下
select s1.course_id,s1.score,count(distinct s2.score)
from score as s1
inner join score as s2
on s1.course_id = s2.course_id and s1.score >= s2.score
group by s1.course_id,s1.score
;
  • 首先将分数表score自连接

    • 按照第一个条件 s1.course_id = s2.course_id把s1表的课程id和s2表的课程id对应起来,但是会产生多余的数据(会将不同sid,student_id,score,但是course_id相同的数据都连接一次),所以需要第二个条件进一步筛选我们的理想数据

    • 按照第二个条件s1.score >= s2.score将分数进行对比,然后进行连接,连接后的结果就是在同一门课程中,将每一个分数与其他分数(包括自己)进行一一对比,只留下大于自己,或者等于自己的分数.

      • 到了这里,经过连接后的表中的内容理想的情况会是,

        • 100分是最高的,所以几乎其他所有分数都符合100>=其他分数 这个条件,所以100分出现次数最多,
        • 又比如0分,是最低分,几乎其他所有分数都不符合0>=其他分数这个条件,所以0分出现的次数应该是最少的,
        • 至此,我们只要按group by s1.course_id,s1.score分组,然后count(s2.score)出现次数从多到少排序可以找到每门课程从高到低的分数了.

        这里再说一下为什么是count(s2.score),而不是count(s1.score),因为我们是按s1.score分组的,如果取count(s1.score),得到的结果都会是1

      • 但是,理想是丰满的,现实却很骨感,由于相同分数情况的出现,单纯的去统计按照

        s1.course_id = s2.course_id and s1.score >= s2.score

        条件连接表的s2.score出现次数并不能准确的排列出最高分和最低分,举个例子说明一下:

        • 比如不同的学生,同一门课程,都是60分,而且这种情况很多,这就会导致我们上面所说的查询方法错误,有可能60出现的次数甚至超过100分,从而导致排序后出现的最高分成了60分.有多少个学生都是同一门课程相同的分数,我们上面所统计的个数就会多几次.
        +-----------+-------+-----------------+
        | course_id | score | count(s2.score) |
        +-----------+-------+-----------------+
        |         1 |     0 |               1 |
        |         1 |    59 |               2 |
        |         1 |    60 |               8 |
        |         1 |    77 |               5 |
        |         1 |    99 |               6 |
        |         1 |   100 |              16 |
        |         2 |     1 |               1 |
        |         2 |    20 |               2 |
        |         2 |    59 |               3 |
        |         2 |    78 |               4 |
        |         2 |    99 |               5 |
        |         2 |   100 |              14 |
        
        # 如上,课目1的60分出现次数超过了77分出现的次数,但是明显60是应该排在77之后的.
        
      • 所以select语句只能写成这样

        select s1.course_id,s1.score,count(distinct s2.score)

      • 要去重!!!

      • 首先通过group by s1.course_id,s1.score分组,将所有相同课程,相同分数的数据分到了一个组里面,通过count(distinct s2.score)中的distinct把重复出现的相同课程,相同分数的数据去掉!!!得到我们想要的数据

      +-----------+-------+--------------------------+
      | course_id | score | count(distinct s2.score) |
      +-----------+-------+--------------------------+
      |         1 |     0 |                        1 |
      |         1 |    59 |                        2 |
      |         1 |    60 |                        3 |
      |         1 |    77 |                        4 |
      |         1 |    99 |                        5 |
      |         1 |   100 |                        6 |
      |         2 |     1 |                        1 |
      |         2 |    20 |                        2 |
      |         2 |    59 |                        3 |
      |         2 |    78 |                        4 |
      |         2 |    99 |                        5 |
      |         2 |   100 |                        6 |
      
      

      得到上面这种数据,我们就可以很方便的取每门课程前几名,或者取最高,最低分数.

    如果需要把最高的分数显示为1,第二的分数显示为2,只需要将语句中的>大于号改成<小于号即可

  • 如果还不能理解的话,建议一步一步加条件查看实际表的数据,来体会每一条条件语句的作用

先看
select *
from score as s1
inner join score as s2
on s1.course_id = s2.course_id ;

再看
select *
from score as s1
inner join score as s2
on s1.course_id = s2.course_id and s1.score >= s2.score;

然后是

select s1.course_id,s1.score,count(s2.score)
from score as s1
inner join score as s2
on s1.course_id = s2.course_id and s1.score >= s2.score
group by s1.course_id,s1.score;

最后是

select s1.course_id,s1.score,count(distinct s2.score)
from score as s1
inner join score as s2
on s1.course_id = s2.course_id and s1.score >= s2.score
group by s1.course_id,s1.score;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • ORACLE自学教程 --create tabletestone ( id number, --序号usernam...
    落叶寂聊阅读 1,121评论 0 0
  • 笔记: 一、聚合函数:计数 最大值 最小值 平均数 求和 1.计数 COUNT() 忽略NULL值 方式1:COU...
    凤之鸠阅读 5,271评论 0 1
  • 不知不觉,毕业很久,你们分开也很久了。 第一次见隔壁班二狗,是高一物理晚自习,他来我们班问老师问题,只记得一个猥琐...
    大雨惊鸿阅读 231评论 0 0
  • 文/萧让 有一次,学校发了工资,我就拿着钱去存钱,在银行等待存钱的时间,很慢,前面有七个人,半个小时都过去了,还没...
    萧让听雪阅读 233评论 0 0
  • 我又想你了,但是你是谁? 我常常觉得自己在想念一个人,一个还未出现在我生命里的人。他或许有白色的胡子,她或许有耀眼...
    荏莘阅读 190评论 0 0