SQL分組查詢找組內(nèi)最大

1、問題

現(xiàn)在有數(shù)據(jù)表,存儲著各個學生的各個科目的成績。如果查出每門課程得分最高的學生該怎么找呢?
正常的思路可能是先按分組查出每個科目的最高成績,然后再將這個結(jié)果和原表做一個連接查詢(按照科目和成績相等連接),這樣才可以查出想要的結(jié)果?;蛘呤?,多寫幾個子查詢,應該也能解決問題。
但是,這些答案都不好,沒有充分發(fā)揮SQL語法中關(guān)系代數(shù)的優(yōu)勢。本文主要講如何通過自連接,解決這個問題。

2、準備

2.1 表結(jié)構(gòu)

建立如下表結(jié)構(gòu):

create table Grade (
    StudentName varchar2(50),
    CourseName varchar2(100),
    Grade number
);

這里省略了主鍵和索引等。

2.2 數(shù)據(jù)

執(zhí)行如下腳本,向表中插入數(shù)據(jù):

INSERT INTO grade (StudentName, CourseName, Grade)
VALUES ('John Smith', 'English', 76);

INSERT INTO grade (StudentName, CourseName, Grade)
VALUES ('John Wall', 'Math', 86);

INSERT INTO grade (StudentName, CourseName, Grade)
VALUES ('Kobe Byrant', 'Math', 90);

INSERT INTO grade (StudentName, CourseName, Grade)
VALUES ('Will Smith', 'English', 96);

INSERT INTO grade (StudentName, CourseName, Grade)
VALUES ('Li Lei', 'Math', 99);


INSERT INTO grade (StudentName, CourseName, Grade)
VALUES ('Tim Duncan', 'English', 95);

COMMIT;

數(shù)據(jù)如下:

STUDENTNAME COURSENAME GRADE
John Smith English 76
John Wall Math 86
Kobe Byrant Math 90
Will Smith English 96
Li Lei Math 99
Tim Duncan English 95

3、查詢語句

3.1 思路

這里的解決方案是做一個自連接(連接條件:t.CourseName = tt.CourseName AND tt.grade >= t.grade),查找比當前記錄分數(shù)高的記錄只有一條的記錄(這一條自然就是記錄本身了)。自連接之后的篩選方法是,先按照t.StudentName, t.CourseName分組,然后,篩選出只有一條記錄的組就好了。

3.2 自連接SQL v1.0

根據(jù)上面的思路,可以寫出如下的SQL:

SELECT t.StudentName,
       t.CourseName
FROM Grade t,
     Grade tt
WHERE t.CourseName = tt.CourseName
  AND tt.grade >= t.grade
GROUP BY t.StudentName,
         t.CourseName
HAVING count(1) <= 1

結(jié)果如下:

STUDENTNAME COURSENAME
Will Smith English
Li Lei Math

3.3 自連接SQL v2.0

前面的結(jié)果中,沒法看到各個科目成績最高的學生的分數(shù),因為分數(shù)字段沒有出現(xiàn)在group by表達式中。
這個非常好解決,不會有相同StudentName和CourseName,但是成績不同的情況,所以,直接在group by子句中加上這個字段就行。如下。

SELECT t.CourseName,
       t.StudentName,
       t.grade
FROM Grade t,
     Grade tt
WHERE t.CourseName = tt.CourseName
  AND tt.grade >= t.grade
GROUP BY t.StudentName,
         t.CourseName,
         t.grade
HAVING count(1) <= 1

查詢結(jié)果如下:

COURSENAME STUDENTNAME GRADE
English Will Smith 96
Math Li Lei 99

3.4 查詢各個科目的成績排名

根據(jù)這個原理,可以很容易地查詢各個科目的成績排名。
自連接之后,有多少條比當前記錄大的(實際是大于等于),就認為排名是第幾。

SELECT t.CourseName,
       t.StudentName,
       t.grade,
       count(1) CourseRank
FROM Grade t,
     Grade tt
WHERE t.CourseName = tt.CourseName
  AND tt.grade >= t.grade
GROUP BY t.StudentName,
         t.CourseName,
         t.grade
order by t.CourseName, CourseRank

結(jié)果如下:

COURSENAME STUDENTNAME GRADE COURSERANK
English Will Smith 96 1
English Tim Duncan 95 2
English John Smith 76 3
Math Li Lei 99 1
Math Kobe Byrant 90 2
Math John Wall 86 3

3.5 Oracle中partition by語句的實現(xiàn)

利用oracle中的partition by語句,可以比較方便的實現(xiàn)按照字段分組,并且在分組內(nèi)按照指定字段排序的需求,如下是一個例子:

SELECT CourseName,
       StudentName,
       Grade,
       rank() over(PARTITION BY CourseName
                   ORDER BY Grade ASC) CourseRank
FROM grade

查詢結(jié)果如下:

COURSENAME STUDENTNAME GRADE COURSERANK
English Will Smith 96 1
English Tim Duncan 95 2
English John Smith 76 3
Math Li Lei 99 1
Math Kobe Byrant 90 2
Math John Wall 86 3

這里的rank函數(shù)可以換成其他的函數(shù),實現(xiàn)不同的排序需求。比如控制兩個相同分數(shù)的排名是否相同、以及如何處理并列排名之后的名次問題等。

4、說明

這個問題,oracle中應該有專門的partition by語法專門解決類似問題,MySql中應該也有GROUP_CONCAT聚合函數(shù)能夠配合SUBSTRING_INDEX截取函數(shù)可以實現(xiàn)類似功能。但是,這些高級的語法有一個局限性,他們不通用。了解這種自連接的思路可以幫助我們更好地理解SQL的強大語法特性。
當然,在工作中,可以根據(jù)具體的環(huán)境,盡量采用專門的語句來實現(xiàn),這樣可讀性會好些。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容