# P21 21. 基础-SQL-DQL-案例练习

> 本页是学习笔记，不等同于视频逐字稿。原声字幕未取得（匿名接口字幕轨为空且需登录，未使用任何账号），也没有逐句讲解记录；讲义只按节与页码对应，切点未逐句确证。

> 以同版讲义的公开镜像核对章节与页码（镜像非官方出品）；非逐字稿，示例为原创补充。

## P21 21. 基础-SQL-DQL-案例练习

- 原视频：https://www.bilibili.com/video/BV1Kr4y1i7ru/?p=21（p=21），时长 11:23
- 讲义：2.6.8 案例（5 题） p48–49（公开镜像；按主题对应，切点 notexact）

- 本集练习数据：practice/p21.sql（先执行，再对照小例）

### 一句话理解

这一集不讲新语法：5 道小题各练一个点——IN + 条件、LIKE 定长、COUNT 分组、双列排序、分页。

### 跟播：视频里 ↔ 本站

视频这一集是「案例」页：5 道小题连着写，题干就在画面上。

| 视频里 / 原课字段 | 本站对应 |
|---|---|
| emp 的 name / age / gender / entrydate | employee 的 name / age / gender / entrydate（本站 6 行） |
| 讲义按「20–23 岁女生」「三个字姓名」出题 | 本站同型改写：条件、定长、分组、排序、分页各一题 |

看完应能：5 道题能独立写出，并说清每道题只练了哪一个点

看视频时以讲义表为准，跟练时用本站表；结构相同、数字不同。

### 步骤

1. 每题先只写「要哪些行」：条件放 WHERE，先别管排序和分页，跑一次看行数对不对。
2. 看到「20、21、22、23 岁」这种离散取值用 IN；看到「姓名是三个字」用 LIKE '___'（下划线一个代表一个字符）。
3. 要「每组一个数」才加 GROUP BY + 聚合函数；只是筛行不要分组。
4. 最后补 ORDER BY（多列就写逗号）和 LIMIT（分页是「跳过几条, 取几条」）。每加一层都跑一次。

### SQL 示例

```
-- 表：department(1 研发部, 2 市场部)
-- employee 6 行：小林/周小舟/陈小南/小李/王小明/赵小北（带 gender、entrydate，本页 5 道题要用）
-- 建表与插入语句见本页练习脚本 p21.sql（独立练习库 mysqlnotes_p21，跑一次就有这两张表）

-- 再加一道综合题：每部门人数与平均薪资，只要均值 > 8500 的组，取均值最高的一组
SELECT dept_id, COUNT(*) AS 人数, ROUND(AVG(salary), 0) AS 平均薪资
FROM employee
WHERE salary IS NOT NULL AND dept_id IS NOT NULL
GROUP BY dept_id
HAVING AVG(salary) > 8500
ORDER BY 平均薪资 DESC
LIMIT 1;
```

说明：【原创演示】小李薪资 NULL 被 WHERE 去掉；赵小北部门是 NULL 也被去掉——不删的话 GROUP BY 会把 NULL 单独算成一组，那组只有他一人、均值 15000，会抢到第一名，答案就跑偏了。剩下部门 1（小林 8000、周小舟 10000，均值 9000）和部门 2（陈小南 12000、王小明 9000，均值 10500），两组都过 HAVING；按均值降序第一名是部门 2：人数 2、平均 10500。

### 分题练习（5 题，每题一个目标）

### 题 1：条件 + IN：找出「女生且年龄是 20/21/22/23 岁」的员工。

```sql
SELECT id, name, age FROM employee
WHERE gender = '女' AND age IN (20, 21, 22, 23)
ORDER BY id;
```

提示：两个条件同时成立用 AND；年龄是几个离散值，用 IN 比写四个 OR 短。

答案（先自己写）：3 行：小林 20、周小舟 21、小李 22。

解释：小李 22 岁也在集合里；王小明 35、赵小北 62 被年龄挡掉，陈小南是男生被 gender 挡掉。IN 后面的括号是值列表，不是子查询也可以。

### 题 2：LIKE 定长：姓名正好三个字。

```sql
SELECT id, name FROM employee
WHERE name LIKE '___'
ORDER BY id;
```

提示：一个下划线 _ 匹配一个字符，三个下划线就是「正好三个字」；用 % 会变成「至少」或「包含」。

答案（先自己写）：4 行：周小舟、陈小南、王小明、赵小北。

解释：小林、小李只有两个字，匹配不上三个下划线。改成 LIKE '__' 就只剩他们两个；改成 LIKE '%小%' 会把名字里带「小」的都找出来，含义完全不同。utf8mb4 下 LIKE 按字符匹配，一个汉字算一个 _。

### 题 3：COUNT + 分组：60 岁以下，男、女各多少人。

```sql
SELECT gender, COUNT(*) AS 人数
FROM employee
WHERE age < 60
GROUP BY gender
ORDER BY gender;
```

提示：先用 WHERE 把 60 岁以上的人筛掉，再按性别分组数行数。

答案（先自己写）：2 行：女 3、男 2。

解释：赵小北 62 岁被 WHERE 去掉，所以男生只剩陈小南、王小明 2 人；女生是小林、周小舟、小李 3 人。如果先分组再想筛年龄，条件就得写进 HAVING，那是另一条路（这里不需要）。

### 题 4：双列排序：按年龄升序，年龄相同再按入职时间降序。

```sql
SELECT name, age, entrydate FROM employee
ORDER BY age ASC, entrydate DESC;
```

提示：ORDER BY 后面用逗号分隔多个列，每一列各写自己的 ASC / DESC；第一列相同才看第二列。

答案（先自己写）：6 行：小林 20 → 周小舟 21 → 小李 22 → 陈小南 23 → 王小明 35 → 赵小北 62。

解释：这份数据里 6 个人的年龄都不同，所以第二列暂时没起作用；把某两人年龄改成一样，才会看到「同龄时入职晚的排前面」。ASC 可省略，DESC 不能省。

### 题 5：分页：按年龄升序，取第 2 页，每页 2 条。

```sql
SELECT name, age FROM employee
ORDER BY age ASC, id ASC
LIMIT 2, 2;
```

提示：LIMIT 起始索引, 条数；起始索引 =（页码 − 1）× 每页条数，第 2 页就是 (2−1)×2 = 2。

答案（先自己写）：2 行：小李 22、陈小南 23。

解释：跳过前 2 条（小林、周小舟），取接下来的 2 条。分页一定要配 ORDER BY，否则每次返回的顺序可能不同，「第 2 页」就没有意义。第一页可简写成 LIMIT 2。

### 易错点

- 把「三个字」写成 LIKE '%%%'：% 是任意长度，三个 % 仍然是「任意」，要用三个下划线。
- ORDER BY age, entrydate DESC 误以为两列都降序：DESC 只作用在紧跟它的那一列。
- GROUP BY 会把 dept_id / gender 为 NULL 的行单独算一组，做统计时先想清楚要不要排除。
- LIMIT 2, 2 读成「第 2 条到第 2 条」：它是「跳过 2 条再取 2 条」。

### 30 秒速记

5 题五个点：IN 选离散值、LIKE '___' 定长、COUNT 配 GROUP BY、多列排序各写方向、LIMIT 跳过+取几条。

### 自测

1. 第 2 题如果把 LIKE '___' 改成 LIKE '%小%'，结果会变成什么？
   - 答：会找出名字里含「小」的人：小林、小李、王小明、赵小北等，含义从「正好三个字」变成「包含某个字」。
2. 第 3 题为什么不用 HAVING？
   - 答：因为筛的是「年龄 < 60」这类行级条件，在分组前用 WHERE 就够了；HAVING 是给分组后的聚合值用的。

### 材料与来源

- [平台视频页] P21 原视频页（BV1Kr4y1i7ru） https://www.bilibili.com/video/BV1Kr4y1i7ru/?p=21
- [官方文档] MySQL 8.0 Reference：SELECT 语句 https://dev.mysql.com/doc/refman/8.0/en/select.html

证据说明：题干见讲义 p48–49「2.6.8 案例」五题（画面 t≈60s 的题目页与讲义逐条对应），本页按同型改写、用自己的 6 行小表，不照抄原题句子与讲义 17 行 emp 数据。原声字幕未取得（匿名接口字幕轨为空），讲师逐句解法未核；示例数字均在 MySQL 8.0.46 实跑核对。
