一道面試題引發的數據庫行列轉換實踐
阿新 • • 發佈:2017-08-22
聚合函數 列數 index 所有 then 重復 一個 mysq 場景
問題場景
最近有個朋友去面試,問了我一道面試題。題目如下,在形如下面的數據庫表score中,找出每門成績(grade)都大於等於80分的學生姓名。
----------------------------------------
name
| course | grade
----------------------------------------
zhangsan | Java | 70
----------------------------------------
zhangsan | C++ | 80
----------------------------------------
lisi | Java | 90
----------------------------------------
lisi | C++ | 60
----------------------------------------
wangwu | Java | 85
----------------------------------------
wangwu | C++ | 95
----------------------------------------
期望結果
----------------------------------------
name
----------------------------------------
wangwu
----------------------------------------
本文以MySQL數據庫為例,以三種方案一步一步實現面試題要求。
方案一
1、尋求行列轉換,也稱矩陣轉置,將多列數據轉為一行,這一步最關鍵,實現SQL語句如下:
下面的sql是以score為主表來構建多列,構造的從表為每門課程的數據表,通過主表的 name 字段來關聯從表的 name 字段。
select s.`name` as name,
(select grade from score where name = s.`name` and course = ‘Java‘)as ‘Java‘,
(select grade from score where name = s.`name` and course = ‘C++‘)as ‘C++‘
from score s
運行結果截圖:
2、對於1的執行結果,需要過濾掉重復的行,只要通過 distinct 關鍵字就可以了,實現SQL語句如下:select distinct s.`name` as name,#此處加distinct來過濾相同的行
(select grade from score where name = s.`name` and course = ‘Java‘)as ‘Java‘,
(select grade from score where name = s.`name` and course = ‘C++‘)as ‘C++‘
from score s
運行結果截圖:
3、最後通過構造一個子查詢,即是把上面2的查詢結果作為一個表來查詢,進行 where 行級過濾就可以了,實現SQL語句如下:
select *
from
(
select distinct s.`name` as name,#此處加distinct來過濾相同的行
(select grade from score where name = s.`name` and course = ‘Java‘)as G1,
(select grade from score where name = s.`name` and course = ‘C++‘)as G2
from score s
) score
where G1>=80 and G2>=80
運行結果截圖:
問題:這裏有一個問題指出下,如果寫成如下的sql語句,把Java和C++作為列名的話(還有C#),查詢結果為NULL,這個問題後續會詳解,請見運行結果截圖:
select *
from
(
select distinct s.`name` as name,#此處加distinct來過濾相同的行
(select grade from score where name = s.`name` and course = ‘Java‘)as ‘Java‘,
(select grade from score where name = s.`name` and course = ‘C++‘)as ‘C++‘
from score s
) score
where ‘Java‘>=80 and ‘C++‘>=80
運行結果截圖:
方案二
1、通過group和聚合函數sum的結合使用,通過group by name分組,利用sum假模假樣計算出每門課程的成績,sum的時候利用case判斷課程類別,就得到了以行顯示的每個學生的每門課程成績,這一步最關鍵,實現SQL語句如下:
select name, sum(case when course=‘Java‘ then grade end) as ‘Java‘, sum(case when course=‘C++‘ then grade end) as ‘C++‘ from score group by name
運行結果截圖:
2、再通過構造一個子查詢,即是把上面1的查詢結果作為一個表來查詢,進行 where 行級過濾就可以了,實現SQL語句如下:
select * from ( select name, sum(case when course=‘Java‘ then grade end) as G1, sum(case when course=‘C++‘ then grade end) as G2 from score group by name ) score where G1>=80 and G2>=80
運行結果截圖:
方案三
1、先找出有任意課程<80分的學生,實現SQL語句如下:
select name from score where grade<80
運行結果截圖:
2、distinct出所有學生列表(不重復),實現SQL語句如下:
select distinct name from score
運行結果截圖:
3、通過構造子查詢從查詢2的結果排除出去查詢1的結果,這一步驟有的數據庫是有集合函數,比如SQL Server的Except,這兒我們用not exists進行行級過濾,實現SQL語句如下:
select * from ( select distinct name from score ) score1 where not exists ( select * from ( select distinct name from score where grade<80 ) score2 where score1.name=score2.name )
運行結果截圖:
總結:
一道面試題引發的數據庫行列轉換實踐