1. 程式人生 > >一道面試題引發的數據庫行列轉換實踐

一道面試題引發的數據庫行列轉換實踐

聚合函數 列數 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
)

運行結果截圖:

技術分享

總結:

一道面試題引發的數據庫行列轉換實踐