目前有需求需要取分組數據的前幾名,有如下的解決方案來實現
具體數據庫如下
SQL寫法:
SELECT
*
FROM
student_grade AS a
WHERE
( SELECT coun ( * ) FROM student_grade AS b WHERE b.subid = a.subid AND b.grade >= a.grade ) <= 2
ORDER BY
a.subid,
a.grade DESC
結果
理解:
核心思路:要算出某人成績在第幾名,可以轉換成:算出他一共比多少人成績高。比如,第一名的人,就沒其它人成績比他更好。第三名的人,就有兩個人成績比他好。
where語句可以理解爲,把表中的每一行記錄,都去與給定的where條件作對比,滿足的再查出來。也就是有個遍歷的過程。
模擬下SQL執行的過程就是,先取出外層a表的第一條記錄
執行where中的子查詢:
select count(1) from student_grade b where b.subId=1 and b.grade>=97
加粗的數據,即從外層a表取出來的。意思是課程1中,有多少個學生的成績是大於等於97的?97已是最高分了,只有一個。因此滿足外層的條件,小於等於2.
接下來取外層表第二條記錄
where子句變成了:
select count(1) from student_grade b where b.subId=1 and b.grade>=93
意思是課程1中,有多少個學生的成績是大於等於93的?結果是2,因爲97和93分都大於等於93分。因此滿足外層的條件,小於等於2.這樣就取出了每門課的前兩名。
接下來取外層表第三條記錄:
where子句變成了:
select count(1) from student_grade b where b.subId=1 and b.grade>=92
意思是課程1中,有多少個學生的成績是大於等於92的?結果是3,因爲97、93、92分都大於等於92分。因此不滿足外層的條件,小於等於2.
以此類推...
擴展一下:
取每組第一名的記錄,只需要把下圖紅框處改爲<=1
取每組成績最低的,只需把下圖黑框處改爲b.grade<=a.grade
此外需要注意的是,該方法在有相同的多個數據時,會導致相同結果全部被選中,此時可以考慮採用另外的方法:
SELECT
s2.stuid,
s2.subid,
s2.grade
FROM
(
SELECT
IF
( s1.subid = @subid, @rank := @rank + 1, @rank := 1 ) AS rank,
@subid := subid AS tmp_subid,
s1.stuid,
s1.subid,
s1.grade
FROM
( SELECT stuid, subid, grade FROM student_grade ORDER BY subid, grade DESC ) s1,
( SELECT @subid := NULL, @rank := 1 ) tmp
) s2
WHERE
s2.rank <= 2