学生写得出 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 团队编写;题目与流程为原创,并非任何学校或出版社的教材。