SQL學習筆記(3)-水平合併(JOIN ON)與垂直合併(UNION)
前言
本系列大多部分來自Hahow的課程-SQL的50道練習,學習過程中得到的筆記,文章內容也有部份都是從教材擷取出來,如果有興趣可以去上課看看。
那這章的基本語法其實網路上的網站fooish都講得蠻完善的了,部分文章內容也會引用網站文章內容。
網路上有人說程式會忘記是很正常的,也因為這次JOIN ON的用法很有趣,所以這次的文章比較多是自己做的圖片,或是個人見解,我希望把程式用白話的方式一一來理解,讓自己能真正的理解他。
開發環境
電腦系統:macOS(Big Sur)
資料庫管理工具:DBeaver
使用語言:SQLite
JOIN ON 水平合併
在JOIN功能中,SQLite分為LEFT JOIN ON
以及JOIN ON
,但其實差別不大,LEFT JOIN ON
的意思是希望保留以左側觀測值為主的資料,但一般來說都會指定要顯示的資料來自於哪個資料表,
所以在SQL中就只有LEFT JOIN ON
而沒有JOIN ON
。
格式
概念圖:
SELECT left_table.column_names,
right_table.column_names
FROM table_name AS left_table
LEFT JOIN table_name AS right_table
ON left_table.join_key = right_table.join_key;
實際使用情境舉例
水平合併的作用在於,今天如果有兩個資料表,一個是
以及學生個人資料表table1資料表,其中裡面存放學生的學號number以及姓名name
學生的考試成績table2資料表,其中存放成績score以及學號number
所以我們想要顯示學生的成績以及姓名時,就需要把兩個欄位一起合併再來顯示,
概念大概是這樣:
number | name | number | score | number | name | score | ||
---|---|---|---|---|---|---|---|---|
1 | A | + | 1 | 90 | = | 1 | A | 90 |
2 | B | + | 2 | 100 | = | 2 | B | 100 |
3 | C | + | 3 | 80 | = | 3 | C | 80 |
上面的例子來說,我們要合併的關鍵是兩個資料表所擁有一樣定義的值,這個值在資料庫的實體關係圖(ER Diagram, Entity Relationship Diagram)找到,這些值就是來自於元資料的定義,那在上面的例子中就是學生的學號,這時學號我們就會稱為他為結合鍵(Join Key),
撰寫流程
在流程上
1.可以先決定左側表格
SELECT
FROM table1 ---先決定有學生學號的左側表格
2.決定右側要合併的表格,以及跟左側表格合併的結合鍵
LEFT JOIN table2 --要合併的表格
ON table2.number = table1.number --結合鍵
3.結合1+2之後,寫上SELECT後面要選擇顯示的資料
SELECT table1.number,table1.name,table2.score
FROM table1
LEFT JOIN table2 --要合併的表格
ON table2.number = table1.number --結合鍵
就完成了,
再來看比較複雜的應用
進階使用
在50道連習題當中,有一題目如下:
從 nba 資料庫查詢各個球隊的得分王(生涯場均得分 ppg 全隊最高)是誰,將查詢結果依隊伍名排序,參考下列的預期查詢結果。
team player ppg
0 Atlanta Hawks Trae Young 24.0
1 Boston Celtics Kemba Walker 19.8
2 Brooklyn Nets Kevin Durant 27.1
3 Charlotte Hornets LaMelo Ball 15.9
4 Chicago Bulls Zach LaVine 18.8
5 Cleveland Cavaliers Collin Sexton 19.7
6 Dallas Mavericks Luka Doncic 25.6
7 Denver Nuggets Nikola Jokic 18.0
8 Detroit Pistons Josh Jackson 12.0
9 Golden State Warriors Stephen Curry 23.8
10 Houston Rockets John Wall 19.0
ER圖:
分析
Career_summaries資料表記錄著用球員id「personId」來做主鍵,裡面放著有關於得分的資料,再來players資料表裡面用「personId」記錄著球員的姓名以及其他球員資料還有所屬球隊的id「teamId」,再來teams資料表裡面用「teamId」記錄著隊伍的資料,所以我們先把希望呈現出來的成果分為左右表格分析,
可以從預期結果裡面來規劃,隊伍連結球員,球員連結得分
撰寫流程
1.在最左側的隊伍名稱在隊伍裡面,所以:
SELECT
FROM teams
2.再來用裡面的teamId來連結players資料表,也因為球員資料裡面記錄著所屬球隊的id:
SELECT --1.
FROM teams
LEFT JOIN players --2.
ON teams .teamId =players .teamId
3.再來,要連結球員個人資料表players,他跟Career_summaries資料表可以用記錄著用球員id「personId」來跟players 資料表中的teamId 做結合鍵。
LEFT JOIN teams --加入
ON players .personId =career_summaries .personId
4.1+2+3變成了:
SELECT --1.
FROM teams
LEFT JOIN players --2.
ON teams .teamId =players .teamId
LEFT JOIN teams --加入 --3
ON players .personId =career_summaries .personId
5.再來會要使用聚合函數MAX來取ppg的最大值,所以一想到聚合函數還有隊伍,就會想到分組的GROUP BY
所以:
SELECT teams .fullName AS team,
players.firstName ||' '||players.lastName AS player,
MAX(career_summaries.ppg)
FROM teams
LEFT JOIN players
ON teams .teamId =players .teamId
LEFT JOIN teams --加入
ON players .personId =career_summaries .personId
GROUP BY teams.teamId
UNION 垂直合併
垂直合併簡單多了,他跟子查詢很像,就是位於資料表上方的SQL語句放UNION上面,要從下方合併的放UNION下面,
A SELECT statement
UNION
Another SELECT statement
使用 UNION
的注意事項
- 垂直合併的欄位數要相同。(如果沒有會產生error)
- 垂直合併的 SQL 若有使用到
ORDER BY
只能放在UNION
之後。 - 垂直合併的重複觀測值會被省略。
如果不要省略重複的觀測值,使用
UNION ALL
A SELECT statement
UNION ALL
Another SELECT statement
就可以了
水平合併&垂直合併的進階使用
在50道連習題當中,有一題目如下:
從twElection2020 資料庫查詢中國國民黨、民主進步黨與親民黨在不分區立委與區域立委的得票率,參考下列的預期查詢結果。
PS 不分區立委的投票資料記錄於資料表 legislative_at_large
,區域立委的投票資料記錄於資料表 legislative_regional。
預期輸出結果:
party | election | votes_percentage |
---|---|---|
中國國民黨 | 不分區立委 | 0.3336 |
民主進步黨 | 不分區立委 | 0.3338 |
親民黨 | 不分區立委 | 0.0366 |
中國國民黨 | 區域立委 | 0.4071 |
民主進步黨 | 區域立委 | 0.4511 |
親民黨 | 區域立委 | 0.0043 |
首先,先觀察個資料表
ER圖:
這題要先知道
不分區立委:簡單說就是政黨的得票,投黨不投人
所以不分區的比較簡單,連接兩種table就好,連結政黨parties資料表,再來這個資料表裡面存放黨派的id「id」,還有政黨名字「party」,透過存放政黨票的資料表 legislative_at_large,有得票數vote以及得票的政黨id「partyid」。
所以結合鍵就是政黨的id,
然後中間用UNION隔開,再來看區域立委:
再來是區域立委:投人的,
所以分區立委的投票率計算方法是:黨派裡面的所有所屬立委的得票數總和,要連接三張table,第一張是黨派的id名單,再來是立委所屬的黨派,再來是立委得票結果,
詳細解答:
SELECT parties.party ,
'不分區立委' election, --4.
ROUNCAST(SUM(legislative_at_large.votes )AS REAL)/(SELECT SUM(votes ) --5
FROM legislative_at_large),4) AS votes_percentage
FROM parties --在最左邊的「黨派」
JOIN legislative_at_large --1.先設定好右側連結表
ON parties.id = legislative_at_large.party_id --2.連結條件
WHERE parties.party IN ('中國國民黨','民主進步黨','親民黨') --3.直接篩選特定黨派
GROUP BY parties.party
UNION -------------------垂直相加第一步,複製上面跟下面,中間用UNION隔開
SELECT parties.party ,
'區域立委' AS election,
ROUND(CAST (SUM(legislative_regional.votes )AS REAL)/(SELECT SUM(legislative_regional.votes )--6
FROM legislative_regional) ,4)AS votes_percentage --6
FROM parties --1左邊的黨派
JOIN candidates --2.要先得知立委所屬黨派,目標是黨派LINK立委LINE立委編號
ON parties.id = candidates.party_id --3.條件(得知立委所屬黨派,黨派表連結立委黨派表)
JOIN legislative_regional --4.加入立委得票數(立委黨派表連結立委得票數表)
ON candidates.id = legislative_regional.candidate_id --5.立委個人id-立委得票id連結
WHERE parties.party IN ('中國國民黨','民主進步黨','親民黨')
GROUP BY parties.party
ORDER BY election ;--垂直相加第二步,排版
差不多就是這樣!