1. 程式人生 > 其它 >T-SQL ->> 找出每個班級裡面任意3門學科總分學科超過290分的學生前3名,按總分排序

T-SQL ->> 找出每個班級裡面任意3門學科總分學科超過290分的學生前3名,按總分排序

前段時間接到國內一家潮流鞋品網購平臺的大資料工程師的面試,第一輪面試對方工程師提問了不少SQL的例子考我。雖然寫了這麼多年SQL,但是居然當中有道題給難住了,電話裡面答不出來,事後自己才解答了出來。我覺得也確實好久沒有遇到這種難的SQL例子,這裡寫下博文記錄一下。

場景:現在有張成績表(score),欄位有班級(class),學生(student),成績(score),期末考試一共有7門學科,期末考試單科滿分是100分,現在要求找出每個班級裡面任意3門學科總分學科超過290分的學生前3名,要求前3名的學生的3科學科成績都按分數高到低排序。

像這種題,拋開效能不談,其實可以有多種T-SQL解法,我腦海裡面想到的思路:子查詢,關聯查詢,開窗聚合函式和CROSS APPLY子句(當然這個是SQL SERVER獨有的)

先建立score表,然後把學生成績插入到表中

  1 CREATE TABLE dbo.score ( [class] nvarchar(10), [student] nvarchar(10), [subject_name] nvarchar(10), [score] decimal(4,1) )
  2 INSERT INTO dbo.score
  3 VALUES
  4 ( N'高一4班', N'馬雲', N'語文', 92.0 ), 
  5 ( N'高一4班', N'馬雲', N'數學', 98.0 ), 
  6 ( N'高一4班', N'馬雲', N'英語', 94.0 ), 
  7
( N'高一4班', N'馬雲', N'政治', 95.0 ), 8 ( N'高一4班', N'馬雲', N'歷史', 92.0 ), 9 ( N'高一4班', N'馬雲', N'物理', 97.0 ), 10 ( N'高一4班', N'馬雲', N'化學', 100.0 ), 11 ( N'高一4班', N'馬雲', N'地理', 100.0 ), 12 ( N'高一4班', N'馬雲', N'生物', 93.0 ), 13 ( N'高一4班', N'馬化騰', N'語文', 98.0 ), 14 ( N'高一4班', N'馬化騰', N'數學', 99.0 ), 15 ( N'
高一4班', N'馬化騰', N'英語', 93.0 ), 16 ( N'高一4班', N'馬化騰', N'政治', 100.0 ), 17 ( N'高一4班', N'馬化騰', N'歷史', 95.0 ), 18 ( N'高一4班', N'馬化騰', N'物理', 96.0 ), 19 ( N'高一4班', N'馬化騰', N'化學', 94.0 ), 20 ( N'高一4班', N'馬化騰', N'地理', 95.0 ), 21 ( N'高一4班', N'馬化騰', N'生物', 98.0 ), 22 ( N'高一4班', N'李彥巨集', N'語文', 93.0 ), 23 ( N'高一4班', N'李彥巨集', N'數學', 96.0 ), 24 ( N'高一4班', N'李彥巨集', N'英語', 92.0 ), 25 ( N'高一4班', N'李彥巨集', N'政治', 92.0 ), 26 ( N'高一4班', N'李彥巨集', N'歷史', 100.0 ), 27 ( N'高一4班', N'李彥巨集', N'物理', 94.0 ), 28 ( N'高一4班', N'李彥巨集', N'化學', 97.0 ), 29 ( N'高一4班', N'李彥巨集', N'地理', 92.0 ), 30 ( N'高一4班', N'李彥巨集', N'生物', 99.0 ), 31 ( N'高一4班', N'王思聰', N'語文', 98.0 ), 32 ( N'高一4班', N'王思聰', N'數學', 95.0 ), 33 ( N'高一4班', N'王思聰', N'英語', 92.0 ), 34 ( N'高一4班', N'王思聰', N'政治', 100.0 ), 35 ( N'高一4班', N'王思聰', N'歷史', 94.0 ), 36 ( N'高一4班', N'王思聰', N'物理', 97.0 ), 37 ( N'高一4班', N'王思聰', N'化學', 96.0 ), 38 ( N'高一4班', N'王思聰', N'地理', 98.0 ), 39 ( N'高一4班', N'王思聰', N'生物', 96.0 ), 40 ( N'高一4班', N'張一鳴', N'語文', 97.0 ), 41 ( N'高一4班', N'張一鳴', N'數學', 99.0 ), 42 ( N'高一4班', N'張一鳴', N'英語', 96.0 ), 43 ( N'高一4班', N'張一鳴', N'政治', 100.0 ), 44 ( N'高一4班', N'張一鳴', N'歷史', 97.0 ), 45 ( N'高一4班', N'張一鳴', N'物理', 95.0 ), 46 ( N'高一4班', N'張一鳴', N'化學', 91.0 ), 47 ( N'高一4班', N'張一鳴', N'地理', 94.0 ), 48 ( N'高一4班', N'張一鳴', N'生物', 98.0 ), 49 ( N'高一4班', N'王興', N'語文', 100.0 ), 50 ( N'高一4班', N'王興', N'數學', 95.0 ), 51 ( N'高一4班', N'王興', N'英語', 96.0 ), 52 ( N'高一4班', N'王興', N'政治', 92.0 ), 53 ( N'高一4班', N'王興', N'歷史', 99.0 ), 54 ( N'高一4班', N'王興', N'物理', 97.0 ), 55 ( N'高一4班', N'王興', N'化學', 100.0 ), 56 ( N'高一4班', N'王興', N'地理', 95.0 ), 57 ( N'高一4班', N'王興', N'生物', 98.0 ), 58 ( N'高一4班', N'劉強東', N'語文', 93.0 ), 59 ( N'高一4班', N'劉強東', N'數學', 96.0 ), 60 ( N'高一4班', N'劉強東', N'英語', 96.0 ), 61 ( N'高一4班', N'劉強東', N'政治', 98.0 ), 62 ( N'高一4班', N'劉強東', N'歷史', 96.0 ), 63 ( N'高一4班', N'劉強東', N'物理', 97.0 ), 64 ( N'高一4班', N'劉強東', N'化學', 97.0 ), 65 ( N'高一4班', N'劉強東', N'地理', 94.0 ), 66 ( N'高一4班', N'劉強東', N'生物', 98.0 ), 67 ( N'高一4班', N'黃錚', N'語文', 94.0 ), 68 ( N'高一4班', N'黃錚', N'數學', 91.0 ), 69 ( N'高一4班', N'黃錚', N'英語', 92.0 ), 70 ( N'高一4班', N'黃錚', N'政治', 100.0 ), 71 ( N'高一4班', N'黃錚', N'歷史', 92.0 ), 72 ( N'高一4班', N'黃錚', N'物理', 93.0 ), 73 ( N'高一4班', N'黃錚', N'化學', 91.0 ), 74 ( N'高一4班', N'黃錚', N'地理', 97.0 ), 75 ( N'高一4班', N'黃錚', N'生物', 100.0 ), 76 ( N'高一4班', N'丁磊', N'語文', 98.0 ), 77 ( N'高一4班', N'丁磊', N'數學', 95.0 ), 78 ( N'高一4班', N'丁磊', N'英語', 95.0 ), 79 ( N'高一4班', N'丁磊', N'政治', 95.0 ), 80 ( N'高一4班', N'丁磊', N'歷史', 91.0 ), 81 ( N'高一4班', N'丁磊', N'物理', 96.0 ), 82 ( N'高一4班', N'丁磊', N'化學', 100.0 ), 83 ( N'高一4班', N'丁磊', N'地理', 94.0 ), 84 ( N'高一4班', N'丁磊', N'生物', 98.0 ), 85 ( N'高一4班', N'雷軍', N'語文', 100.0 ), 86 ( N'高一4班', N'雷軍', N'數學', 92.0 ), 87 ( N'高一4班', N'雷軍', N'英語', 98.0 ), 88 ( N'高一4班', N'雷軍', N'政治', 95.0 ), 89 ( N'高一4班', N'雷軍', N'歷史', 94.0 ), 90 ( N'高一4班', N'雷軍', N'物理', 99.0 ), 91 ( N'高一4班', N'雷軍', N'化學', 97.0 ), 92 ( N'高一4班', N'雷軍', N'地理', 96.0 ), 93 ( N'高一4班', N'雷軍', N'生物', 96.0 ), 94 ( N'高一4班', N'周鴻禕', N'語文', 91.0 ), 95 ( N'高一4班', N'周鴻禕', N'數學', 94.0 ), 96 ( N'高一4班', N'周鴻禕', N'英語', 96.0 ), 97 ( N'高一4班', N'周鴻禕', N'政治', 99.0 ), 98 ( N'高一4班', N'周鴻禕', N'歷史', 93.0 ), 99 ( N'高一4班', N'周鴻禕', N'物理', 99.0 ), 100 ( N'高一4班', N'周鴻禕', N'化學', 93.0 ), 101 ( N'高一4班', N'周鴻禕', N'地理', 100.0 ), 102 ( N'高一4班', N'周鴻禕', N'生物', 99.0 ), 103 ( N'高一4班', N'張朝陽', N'語文', 95.0 ), 104 ( N'高一4班', N'張朝陽', N'數學', 95.0 ), 105 ( N'高一4班', N'張朝陽', N'英語', 94.0 ), 106 ( N'高一4班', N'張朝陽', N'政治', 95.0 ), 107 ( N'高一4班', N'張朝陽', N'歷史', 98.0 ), 108 ( N'高一4班', N'張朝陽', N'物理', 93.0 ), 109 ( N'高一4班', N'張朝陽', N'化學', 94.0 ), 110 ( N'高一4班', N'張朝陽', N'地理', 94.0 ), 111 ( N'高一4班', N'張朝陽', N'生物', 91.0 ), 112 ( N'高一4班', N'俞敏洪', N'語文', 91.0 ), 113 ( N'高一4班', N'俞敏洪', N'數學', 95.0 ), 114 ( N'高一4班', N'俞敏洪', N'英語', 100.0 ), 115 ( N'高一4班', N'俞敏洪', N'政治', 99.0 ), 116 ( N'高一4班', N'俞敏洪', N'歷史', 91.0 ), 117 ( N'高一4班', N'俞敏洪', N'物理', 97.0 ), 118 ( N'高一4班', N'俞敏洪', N'化學', 93.0 ), 119 ( N'高一4班', N'俞敏洪', N'地理', 93.0 ), 120 ( N'高一4班', N'俞敏洪', N'生物', 94.0 ), 121 ( N'高一4班', N'張磊', N'語文', 99.0 ), 122 ( N'高一4班', N'張磊', N'數學', 99.0 ), 123 ( N'高一4班', N'張磊', N'英語', 92.0 ), 124 ( N'高一4班', N'張磊', N'政治', 98.0 ), 125 ( N'高一4班', N'張磊', N'歷史', 97.0 ), 126 ( N'高一4班', N'張磊', N'物理', 99.0 ), 127 ( N'高一4班', N'張磊', N'化學', 100.0 ), 128 ( N'高一4班', N'張磊', N'地理', 91.0 ), 129 ( N'高一4班', N'張磊', N'生物', 100.0 ), 130 ( N'高一4班', N'沈南鵬', N'語文', 96.0 ), 131 ( N'高一4班', N'沈南鵬', N'數學', 92.0 ), 132 ( N'高一4班', N'沈南鵬', N'英語', 93.0 ), 133 ( N'高一4班', N'沈南鵬', N'政治', 91.0 ), 134 ( N'高一4班', N'沈南鵬', N'歷史', 94.0 ), 135 ( N'高一4班', N'沈南鵬', N'物理', 100.0 ), 136 ( N'高一4班', N'沈南鵬', N'化學', 95.0 ), 137 ( N'高一4班', N'沈南鵬', N'地理', 91.0 ), 138 ( N'高一4班', N'沈南鵬', N'生物', 97.0 )
View Code

思路是先分析輸出結果和排序條件,這兩者影響整個SQL的寫法(因為在輸出結果和排序中出現的欄位都需要在整個SQL中保留,影響輸出結果的行顆粒度)。然後把條件拆開來,找到分組單位,聚合條件、過濾和排序規則

1、輸出結果是 前3名學生的3科學科成績按學生總分,其次學科成績高低排序。

2、分組單位是學生

3、任意3門學科總分超過XXX分,意味著是連續聚合,需要從高到低排序並聚合

4、排序取班級前3名

PS.輸出結果和排序條件,這兩者影響整個SQL的寫法,因為在輸出結果和排序中出現的欄位都需要在整個SQL中保留,影響輸出結果的行顆粒度。首先說輸出結果,要求只輸出學生姓名和3科總成績(那就是3行資料,每個學生一行),這種是寫法上最簡單的,直接分組聚合就可以了,難點在任意3門學科總分處理上。如果是要求學生姓名和分別3科學科成績(即上面的例子),那就是每個學生+3門學科都要展示出來。還有要求你把全部學科成績都展示出來,但是條件時任意3門學科總分要滿足條件。甚至還有更加變態的,要你把每個學生的3門學科以及其他學科成績彙總成一行(即,每個學生4行資料)。不同的結果展示都直接影響過程的SQL程式碼寫法。原則肯定是最終資料越少越好,資料越多且還需要一些聚合處理的,程式碼越複雜。當然有些細節點是相似的,例如說任意3科成績前3,這個肯定無論如何都是要通過排序然後聚合彙總的。先對每個學生的學科成績進行從高到低的排序,然後取前3高分的學科進行彙總聚合,剔除不滿足總分要求的學生。

再說排序條件對SQL寫法的影響,舉個例子:最終結果只要求輸出3科總分成績,因為這個是硬過濾條件,但是學生排序是所有學科的平均分\總分\甚至特定學科的分數排序。不要以為不可能,以前工作中做過一些的財務報表,報表的排序就是要求用聚合表外的資訊欄位來排序,有些排序規則還是極其複雜,需要用各種CASE WHEN表示式堆的。

案例1:每個班級任意3門學科總分成績超過290分的學生,取前3名,相同總分成績按照3門學科總分裡面所含語數英三科成績總分高低排序

DECLARE @p_total DECIMAL(12,1) = 290

SELECT
 S.[class]
,S.[student]
,S.[subject_name]
,S.[score]
,[TOTAL_SCORE]
,ROW_NO
FROM (
    SELECT [class]
        ,[student]
        ,[TOTAL_SCORE]
        ,[TOTAL_SCORE_YSY]
        ,ROW_NUMBER() OVER (PARTITION BY [class] ORDER BY [TOTAL_SCORE] DESC,[TOTAL_SCORE_YSY] DESC) AS ROW_NO
    FROM (
        SELECT
         [class]
        ,[student]
        ,SUM(CASE WHEN [subject_name] IN (N'語文',N'數學',N'英語') THEN [score] END) AS [TOTAL_SCORE_YSY]
        ,SUM([score]) AS [TOTAL_SCORE]
        FROM (
            SELECT [class]
                  ,[student]
                  ,[subject_name]
                  ,[score]
                  ,ROW_NUMBER() OVER(PARTITION BY [class],[student] ORDER BY [score] DESC) AS STU_RN
            FROM [dbo].[score]
        ) AS B
        WHERE B.STU_RN <= 3
        GROUP BY [class]
        ,[student]
) AS T) AS TOP_3
INNER JOIN (
    SELECT
         [class]
        ,[student]
        ,[subject_name]
        ,[score]
        FROM (
            SELECT [class]
                  ,[student]
                  ,[subject_name]
                  ,[score]
                  ,ROW_NUMBER() OVER(PARTITION BY [class],[student] ORDER BY [score] DESC) AS STU_RN
            FROM [dbo].[score]
    ) AS B
    WHERE B.STU_RN <= 3
) AS S ON S.[student] = TOP_3.[student] 
AND S.[class] = TOP_3.[class]
AND TOP_3.ROW_NO <= 3
WHERE [TOTAL_SCORE] >= @p_total
ORDER BY [class],ROW_NO,TOP_3.TOTAL_SCORE