PPT 03 SQL 语句
2. PPT 03 SQL 语句:把自然语言翻译成查表动作¶
这一章以 第三章 introduction to SQL.docx 为主要讲解来源。PPT 给了 SQL 的题型范围,但 Word 笔记里真正有用的是那些“为什么这么写”的解释:外键删除/更新策略、natural join 的坑、分组和 having、嵌套查询、some/all/exists。
本章只抓一件事:
2.1 SQL 做题先问四句话¶
任何 SQL 题都先别写代码,先翻译题意:
1. 最后输出什么? -> SELECT
2. 信息在哪些表里? -> FROM
3. 表和表怎么接上? -> JOIN 条件 / WHERE 连接条件
4. 需要筛选/分组/排序吗? -> WHERE / GROUP BY / HAVING / ORDER BY
例子:
你要先在脑子里定位:
三张表要靠共同钥匙接起来:
所以答案是:
SELECT s.name
FROM student AS s, takes AS t, course AS c
WHERE s.sid = t.sid
AND t.cid = c.cid
AND s.major = 'CS'
AND c.title = 'Database'
AND t.score >= 85;
这类题的考场第一笔:
2.2 建表、主键、外键:别只会写 CREATE TABLE¶
Word 笔记里强调了外键的真实含义:一张表中的某列必须能在另一张表的主键列里找到。
比如:
CREATE TABLE instructor (
ID CHAR(5),
name VARCHAR(20),
dept_name VARCHAR(20),
salary NUMERIC(8,2),
PRIMARY KEY (ID),
FOREIGN KEY (dept_name) REFERENCES department(dept_name)
);
这句话不是装饰。它的意思是:
如果某个系撤销了,department 里要删除这个系,但 course 或 instructor 里还有行指向它,数据库就要决定怎么办。这就是外键的 delete / update 策略。
2.3 外键 delete / update 四种策略¶
假设:
如果一个 department 被删除,course 里相关课程怎么办?
| 策略 | 人话 | 例子 |
|---|---|---|
cascade |
跟着一起删/改 | 系撤销,属于这个系的课程也删除 |
set null |
指向它的外键改成 NULL | 课程暂时没有所属系 |
restrict |
不允许删/改被引用的行 | 还有课程属于这个系,所以不能删系 |
set default |
改成默认值 | 课程所属系改成默认系 |
更新也是同理。如果 department 改名,cascade 就让引用它的 course 也跟着改名。
易错点:
2.4 natural join:它很方便,也很危险¶
Word 笔记里有一个非常重要的坑:natural join 会自动拿所有同名属性做等值连接。
比如:
如果你写:
数据库可能会用两个同名列一起连:
这可能变成:
但题目如果是“找上了不是自己系课程的学生”,这就错了。更稳的写法是显式写连接条件:
SELECT s.name
FROM student AS s, takes AS t, course AS c
WHERE s.ID = t.ID
AND t.course_id = c.course_id
AND s.dept_name <> c.dept_name;
A4 规则:
2.5 LIKE 和转义字符¶
LIKE 用来做字符串匹配:
含义:
符号:
如果你真的要找字符串里的 %,就要转义。比如找名字等于 100% 的行,不能写:
这会让第二个 % 仍然被当作通配符。应该写:
意思是:
2.6 排序和 limit¶
默认升序。
降序。
多个排序字段:
意思是:
LIMIT 常用来取前几名:
如果写:
含义是:
注意:如果题目要求“并列第一全部输出”,不要用 LIMIT 1,因为并列会被截断。
2.7 集合操作:默认去重¶
SQL 的集合操作:
默认会去重。如果不想去重,要写:
这点常出选择题。
2.8 NULL:它不是 0,也不是空字符串¶
NULL 表示未知或不存在。
不能写:
要写:
或者:
聚合函数里:
2.9 GROUP BY:看到“每个”就分组¶
看到这些词,先想到分组:
例子:
重要规则:
所以这个是错的:
因为每个系有很多老师,分组后数据库不知道该显示哪个 name。
2.10 WHERE 和 HAVING:一个筛行,一个筛组¶
例子:
SELECT dept_name, COUNT(*) AS cnt
FROM instructor
WHERE salary > 100000
GROUP BY dept_name
HAVING COUNT(*) > 10;
这句话分三步读:
如果你想筛聚合结果,就必须用 HAVING。
2.11 IN / NOT IN:结果在不在一个集合里¶
人话:
NOT IN 反过来。但要小心 NULL:如果子查询结果里有 NULL,NOT IN 可能变得很别扭。考试如果要表达“不存在”,更稳的是 NOT EXISTS。
2.12 SOME / ALL:和一堆值比较¶
Word 笔记里提到:如果子查询只返回一个值,可以直接比较:
但如果子查询可能返回多个值,就要写 SOME 或 ALL。
找 CS 系 credits 最大的课程,可以写:
SELECT *
FROM course
WHERE department = 'CS'
AND credits >= ALL (
SELECT credits
FROM course
WHERE department = 'CS'
);
这句的意思:
2.13 EXISTS:有没有这样一行¶
EXISTS 不关心子查询具体返回什么,只关心有没有行。
例子:
可以写:
SELECT C1.title
FROM course AS C1
WHERE EXISTS (
SELECT *
FROM course AS C2
WHERE C2.title = C1.title
AND C2.course_id <> C1.course_id
);
人话:
2.14 “每组最大/平均以上”题型模板¶
2025 review 很喜欢这种题。
例题:
先写每组女性人数:
SELECT group_name, COUNT(user_id) AS female_count
FROM user
WHERE gender = 'female'
GROUP BY group_name;
再找最大:
WITH T AS (
SELECT group_name, COUNT(user_id) AS female_count
FROM user
WHERE gender = 'female'
GROUP BY group_name
)
SELECT group_name
FROM T
WHERE female_count = (
SELECT MAX(female_count)
FROM T
);
这个写法比 ORDER BY ... LIMIT 1 更稳,因为并列第一也能全部输出。
再看“高于平均”:
核心是先造一张临时表:
再和平均值比较:
WITH T AS (
SELECT u.user_id, COUNT(f.follower_id) AS follower_count
FROM user AS u, followship AS f
WHERE u.user_id = f.user_id
AND u.group_name = 'game'
GROUP BY u.user_id
)
SELECT u.user_id, u.name, T.follower_count
FROM user AS u, T
WHERE u.user_id = T.user_id
AND T.follower_count > (
SELECT AVG(follower_count)
FROM T
);
考场第一笔:
2.15 SQL A4 规则¶
SQL 四问:输出什么、来自哪些表、怎么连接、怎么筛选/分组。
外键策略:cascade / set null / restrict / set default。
natural join 会用所有同名列,慎用。
LIKE 中 % 和 _ 是通配符;查真实 % 要 ESCAPE。
GROUP BY 后,SELECT 普通列必须出现在 GROUP BY 中。
WHERE 筛原始行,HAVING 筛分组结果。
子查询多值比较用 SOME / ALL。
EXISTS 只问“有没有行”。
每组最大/平均以上:先构造分组统计表 T,再比较。