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;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章