sql題,選出每門科目前三名的學生
一、常見資料庫
關係型資料庫:Oracle、DB2、SQLServer、MySQL
非關係型資料庫:MongoDB
建立成績表並插入資料:
create table StuScore(StuId int,Subject nvarchar(10),Score float); insert into StuScore values(1,'語文',70); insert into StuScore values(1,'數學',60); insert into StuScore values(1,'英語',90); insert into StuScore values(2,'語文',78); insert into StuScore values(2,'數學',67); insert into StuScore values(2,'英語',80); insert into StuScore values(3,'語文',89); insert into StuScore values(3,'數學',60); insert into StuScore values(3,'英語',97); insert into StuScore values(4,'語文',50); insert into StuScore values(4,'數學',67); insert into StuScore values(4,'英語',70); insert into StuScore values(5,'語文',79); insert into StuScore values(5,'數學',65); insert into StuScore values(5,'英語',79); insert into StuScore values(6,'語文',74); insert into StuScore values(6,'數學',56); insert into StuScore values(6,'英語',87);
二、Oracle完成
用oracle的語言比較簡潔,目前在 MSSQLServer、Oracle、DB2 等主流資料庫中都提供了對開窗函式的支援,不過 MYSQL 暫時還未對開窗函式給予支援。
1、開窗函式
與聚合函式一樣,開窗函式也是對行集組進行聚合計算,但是它不像普通聚合函式那樣每組只返回一個值,開窗函式可以為每組返回多個值,因為開窗函式所執行聚合計算的行集組是視窗。
開窗函式格式: 函式名(列) OVER(選項)
OVER 關鍵字後的括號中還經常新增選項用以改變進行聚合運算的視窗範圍。如果OVER 關鍵字後的括號中的選項為空,則開窗函式會對結果集中的所有行進行聚合運算。
例子:
select fname, fcity, fsalary, count(*) over() 工資小於5000員工數
from t_person
where fsalary < 5000
返回所有滿足工資小於5000的記錄,通過聚合函式count計算這些記錄的個數。
如果用MySQL實現就比較麻煩:
select fname,fcity,fsalary,
(select count(*) from t_person where fsalary < 5000) 工資少於5000員工總數
from t_person
where fsalary < 5000;
結果如下:
2、partition by
開窗函式的 OVER 關鍵字後括號中的可以使用 PARTITION BY 子句來定義行的分割槽來供進行聚合計算。類似於GROUP BY,但與 GROUP BY 子句不同是,PARTITION BY 子句建立的分割槽是獨立於結果集的,建立的分割槽只是供進行聚合計算的,而且不同的開窗函式所建立的分割槽也不互相影響。
3、row_number()
除了可以在開窗函式中使用COUNT()、SUM()、MIN()、MAX()、AVG()等這些聚合函式,還可以在開窗函式中使用一些高階的函式,有些函式同時被DB2和Oracle同時支援,比如RANK()、DENSE_RANK()、ROW_NUMBER(),而有些函式只被Oracle支援,比如RATIO_TO_REPORT()、NTILE()、LEAD()、LAG()、FIRST_VALUE()、LAST_VALUE()。
row_number():從1開始,為每一條分組記錄返回一個數字,返回的主要是“行”的資訊,並沒有排名
語法:ROW_NUMBER() OVER (PARTITION BY COL1 ORDER BY COL2)
功能:表示根據COL1分組,在分組內部根據 COL2排序,而這個值就表示每組內部排序後的順序編號(組內連續的唯一的)
4、解決問題
語法:
select * from
(select *,row_number() over(partition by Subject order by Score desc) rn
from StuScore)
where rn<4;
按照科目分割槽,將每個科目按照成績從大到小排序並給出順序號(不同科目順序重新從1開始),在這個結果集上取每個科目前三個。
注意:where篩選在新結果集上進行的,以下語句是錯誤的:
select * from
(select *,row_number() over(partition by Subject order by Score desc) rn
from StuScore
where rn<4);
因為在子查詢中,根據語句執行順序,先執行where篩選,在進行欄位選擇、聚合函式選擇,因此還未執行row_number() over(partition by Subject order by Score desc) rn就出現rn進行篩選,是有誤的。
補充子查詢:
• 出現在select子句中:將子查詢返回結果作為主查詢的一個欄位或者計算值(標量子查詢、列子查詢)
• 出現在where/having子句中:將子查詢返回的結果作為主查詢的條件(標量子查詢、行子查詢、列子查詢、表子查詢)
• 出現在from或join子句中:將子查詢返回的結果作為主查詢的一個表(標量子查詢、行子查詢、列子查詢、表子查詢)
備註:子查詢必須新增表別名,如果需要引用表子查詢中的計算欄位,必須新增列別名才可以引用
三、MySQL實現
由於MySQL是沒有開窗函式等函式,因此比較複雜。
語法:
SELECT *
FROM StuScore s
WHERE EXISTS(SELECT COUNT(*) FROMStuScore ss WHERE ss.Score>=s.Scoreand ss.Subject=s.SubjectGROUP BY ss.Subject HAVING COUNT(*)<=3)
ORDER BY Subject,Score DESC;
子查詢思路如下:
s表和ss表是同一張表,將得到的結果按照科目(主排序關鍵字)、成績(次排序關鍵字)
補充order by:
(1)多欄位排序是按照一個欄位一個欄位進行,先安排第一個欄位排序,如果第一個欄位一樣,才利用第二個欄位進行排序。如下圖:
(2)order by code,name desc不等同於order by code asc, name desc
實際上MySQL會先以code進行降序排序,在code進行降序排序的基礎上,再使用name進行降序排序
(3)如果想不同欄位不同排序標準,需要在每個欄位後面加上排序準則,如:order by code asc, name desc
MySQL就會先以code進行升序排序,在code進行升序排序的基礎上,再使用name進行降序排序
詳細學習版參見:https://www.cnblogs.com/lihaoyang/p/6756956.html