學生寫得出 SELECT * FROM student,但一加 GROUP BY 就把欄位亂放,一問 WHERE 與 HAVING 的分別就沉默。這一堂用一條固定書寫次序與一份 120 行的小資料表,讓每一條查詢都可以逐步驗算,而不是靠背句型。
本堂目標 學生能夠:① 按固定次序寫出 6 個子句(SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY),並指出書寫次序與執行次序不同;② 用 120 名學生分 5 班(每班 24 人、平均分 62)的資料表,寫出每班人數、平均分與總分的聚合查詢,並正確使用 WHERE(過濾行)與 HAVING(過濾組);③ 分辨 4 種 JOIN 的保留側,並解釋主鍵與外鍵在連結兩表時的角色。
01 先備知識(開課前 3 分鐘快檢)
以下任何一項未過關,本堂就會卡住,先補該項:
| 技能 |
快檢題 |
過關標準 |
| 資料表概念 |
一列與一行分別代表甚麼? |
一行是一筆記錄,一列是一個欄位 |
| 主鍵 |
主鍵可以有兩個相同值嗎? |
不可以,必須唯一 |
| 基本 SELECT |
SELECT * 的意思是? |
取出所有欄位 |
| 比較運算 |
SQL 的「不等於」可以寫成甚麼? |
<> 或 != |
02 課堂流程(60 分鐘)
| 時間 |
環節 |
導師做什麼 |
學生做什麼 |
| 0–6 分 |
一張表三件事 |
欄位、記錄、主鍵 |
先畫表再寫句 |
| 6–16 分 |
六個子句次序 |
6 個子句固定排位 |
書寫與執行不同 |
| 16–30 分 |
WHERE 與 HAVING |
過濾行 vs 過濾組 |
聚合條件放 HAVING |
| 30–44 分 |
聚合與分組 |
120 人 ÷ 5 班 = 24 |
GROUP BY 之後只可選分組欄 |
| 44–54 分 |
四種 JOIN |
4 種保留側 |
INNER 兩邊都要有 |
| 54–60 分 |
查詢驗算與離場檢查 |
用行數與總分核對答案 |
算得出才算對 |
03 講解腳本(照著說就可以)
第一幕:先畫表,再寫句
在白板畫兩欄三行的學生表與班別表,標明主鍵與外鍵。然後才寫第一句 SELECT。理由很實際:學生寫錯多數不是語法,是心裏沒有一張表,於是 GROUP BY 之後仍然選 student_name,因為他以為自己在「篩選」。有表在手,他會見到名字是逐行的,班別才是逐組的。
問:GROUP BY class 之後,SELECT student_name 為甚麼錯?(名字是逐行的,不是逐組的)
第二幕:用書寫次序與執行次序分開講
書寫次序固定 6 個子句,但資料庫執行時先 FROM 取表,再 WHERE 過濾行,再 GROUP BY 分組,再 HAVING 過濾組,再 SELECT 取欄,最後 ORDER BY 排序。所以 WHERE 一定不可以用聚合函數(那時還未分組),HAVING 一定不可以過濾未分組的原始行。這一條講清楚,六成錯誤即時消失。
問:COUNT(*) > 20 應該放 WHERE 還是 HAVING?(HAVING)
第三幕:最後用行數驗算
給 120 名學生、5 班、平均分 62,總分就是 7440。學生寫完查詢後要用兩個數自我檢查:每班 24 人(120 除以 5),全班總分相加要等於 7440。若加起來不等,多數是 JOIN 造成重複行,這是 JOIN 最常見的陷阱,用算術一捉就中。
問:JOIN 之後總分變大,最可能是甚麼原因?(一對多造成重複計數)
04 必教題型與步驟
| 題型 |
例子 |
步驟 |
答案 |
| 單表篩選 |
SELECT name FROM student WHERE form = ‘S6’ |
選欄、取表、加條件,字串要引號 |
只回傳符合條件的行 |
| 聚合與分組 |
每班人數與平均分 |
SELECT class, COUNT(*), AVG(mark) … GROUP BY class |
五組,每組 24 人 |
| 分組後篩選 |
平均分高於 60 的班 |
聚合條件寫在 HAVING,不寫 WHERE |
HAVING AVG(mark) > 60 |
| 兩表連結 |
學生表與班別表取班主任 |
寫 ON 的連接條件,指明保留側 |
INNER 只留兩邊都有的 |
合理性檢查(一定要教):子句次序固定,寫錯位資料庫直接報錯;WHERE 在分組之前,所以不可用聚合函數;HAVING 在分組之後,所以可以寫 COUNT 與 AVG;SELECT 之後的非聚合欄位必須出現在 GROUP BY;120 除以 5 得 24,這是驗算行數的錨;總分 7440 是另一個錨,JOIN 後變大代表重複計數;JOIN 一定要寫 ON,否則變成笛卡兒積;字串用單引號,數值不加引號;COUNT(*) 數行,COUNT(欄位) 不數 NULL。
05 獨立練習三題
| 題目 |
答案 |
想觀察什麼 |
| 寫查詢:列出平均分高於 65 的班別與人數 |
SELECT class, COUNT(*) FROM student GROUP BY class HAVING AVG(mark) > 65; |
聚合條件在 HAVING,不是 WHERE |
| 判斷:WHERE COUNT(*) > 10 可行嗎? |
不可行 |
WHERE 執行時尚未分組,沒有 COUNT 可用 |
| 計算:兩表 JOIN 後行數變 240,原因? |
一對多關係造成重複 |
要加 DISTINCT 或先聚合 |
06 學生常見錯誤與糾正句
| 錯誤 |
學生通常這樣寫 |
導師這樣說 |
| WHERE 用聚合函數 |
學生寫 WHERE COUNT(*) > 20 |
導師原句:「WHERE 行的時候還未分組,COUNT 未存在;要數組,就用 HAVING。」 |
| GROUP BY 後選了逐行欄位 |
學生寫 SELECT name, class, COUNT(*) … GROUP BY class |
導師原句:「分組之後,每個班只剩一行,你叫它填邊個名字?名字要逐行才有。」 |
| JOIN 沒寫 ON |
學生寫 FROM student JOIN class |
導師原句:「沒寫 ON,資料庫就當你要兩表全配對,行數會爆炸,答案一定錯。」 |
| COUNT(欄位) 與 COUNT(*) 混用 |
學生用 COUNT(phone) 數人數,結果少了 |
導師原句:「COUNT(欄位) 不數 NULL,電話空白的人就消失了;數人要 COUNT(*)。」 |
07 家課建議(直接指到練習卷題號)
08 離場檢查(最後 5 分鐘)
| 題目 |
答案 |
判定 |
| 聚合條件寫在哪個子句? |
HAVING |
答 WHERE=分組次序未掌握 |
| GROUP BY 之後可選甚麼欄位? |
分組欄位與聚合函數 |
答任何欄位=逐行與逐組混淆 |
| INNER JOIN 保留哪一側? |
兩邊都有的記錄 |
答全部=保留側概念未建立 |
09 導師常問
適合哪一班?
中六 ICT 資料庫單元;中五下學期可先教單表 SELECT。
要背幾多語法?
六個子句次序與四個聚合函數,其餘由執行次序推出。
有無答案?
頁內三題連答案與解說,家課列三個站內章節。
立即免費搵導師導師登記 →
本頁為 TutorZone 導師備課指引,內容由 TutorZone 團隊編寫;題目與流程為原創,並非任何學校或出版社的教材。