6.1 单关系数据查询 · 上机练习

跟着做,学 SELECT

从零开始:创建数据库 → 建表 → 插入数据 → 逐步练习查询结构、无条件查询和条件查询的全部知识点。每一步都有可复制的 SQL 代码和期望结果。

1建库建表
2插入数据
3练习查询
阶段一

环境准备 — 创建数据库

在 MySQL 中创建 teaching 数据库并切换到该数据库

1
创建并使用数据库 teaching
必做

首先登录 MySQL,然后创建 teaching 数据库。如果之前已存在同名数据库,先删除再创建。

SQL
-- 如果已存在则先删除(注意:会丢失旧数据!) DROP DATABASE IF EXISTS teaching; -- 创建数据库,指定字符集 CREATE DATABASE teaching DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; -- 切换到 teaching 数据库 USE teaching;
💡 utf8mb4 是 MySQL 中支持完整中文和 emoji 的字符集,推荐使用。
阶段二

建表并插入数据

创建课程表(c)、学生表(s)、教师表(t)、选课表(sc)、教师授课表(tc),并插入示例数据

2
创建课程表 c 并插入数据
必做

课程表包含 3 个字段:课程号(cno)、课程名(cn)、课时(ct)。

SQL — 建表
CREATE TABLE c ( cno VARCHAR(10) PRIMARY KEY, cn VARCHAR(30) NOT NULL, ct INT );
SQL — 插入数据
INSERT INTO c VALUES ('c1', 'Java程序设计', 40), ('c2', '程序设计基础', 48), ('c3', '线性代数', 48), ('c4', '数据结构', 64), ('c5', '数据库系统', 56), ('c6', '数据挖掘', 32), ('c7', '高等数学', 60), ('c8', '控制理论', 32);
✓ 验证:SELECT * FROM c;
cnocnct
c1Java程序设计40
c2程序设计基础48
c3线性代数48
c4数据结构64
c5数据库系统56
c6数据挖掘32
c7高等数学60
c8控制理论32
3
创建学生表 s 并插入数据
必做

学生表包含:学号(sno)、姓名(sn)、性别(sex)、年龄(age)、专业(maj)、学院(dept)。

SQL — 建表
CREATE TABLE s ( sno VARCHAR(10) PRIMARY KEY, sn VARCHAR(20) NOT NULL, sex VARCHAR(4), age INT, maj VARCHAR(30), dept VARCHAR(30) );
SQL — 插入数据
INSERT INTO s VALUES ('s1', '王彤', '女', 18, '计算机', '信息学院'), ('s2', '苏乐', '男', 20, '信息', '信息学院'), ('s3', '林欣怡', '女', 19, '信息', '信息学院'), ('s4', '张超', '男', 20, '自动化', '信息学院'), ('s5', '魏立', '男', 17, '数学', '理学院'), ('s6', '何欣荣', '女', 21, '计算机', '信息学院'), ('s7', '赵琳琳', '女', 19, '数学', '理学院'), ('s8', '陈亮', '男', 20, '自动化', '信息学院');
4
创建教师表 t 并插入数据
必做

教师表包含:教师号(tno)、姓名(tn)、性别(sex)、年龄(age)、职称(prof)、专业(maj)、学院(dept)。

SQL — 建表
CREATE TABLE t ( tno VARCHAR(10) PRIMARY KEY, tn VARCHAR(20) NOT NULL, sex VARCHAR(4), age INT, prof VARCHAR(20), maj VARCHAR(30), dept VARCHAR(30) );
SQL — 插入数据
INSERT INTO t VALUES ('t1', '刘杨', '男', 35, '教授', '计算机', '信息学院'), ('t2', '周建', '男', 28, '讲师', '数学', '理学院'), ('t3', '顾伟', '男', 38, '副教授', '计算机', '信息学院'), ('t4', '赵礼', '男', 52, '教授', '自动化', '信息学院'), ('t5', '赵希希', '女', 31, '副教授', '信息', '信息学院'), ('t6', '张刚', '男', 30, '讲师', '自动化', '信息学院');
5
创建选课表 sc 并插入数据
必做

选课表包含:学号(sno)、课程号(cno)、成绩(score)。注意部分成绩为 NULL(学生选了课但没考试)。

SQL — 建表
CREATE TABLE sc ( sno VARCHAR(10), cno VARCHAR(10), score DECIMAL(5,2), PRIMARY KEY (sno, cno) );
SQL — 插入数据
INSERT INTO sc VALUES ('s1', 'c1', 90.50), ('s1', 'c2', 80.00), ('s1', 'c3', 85.00), ('s2', 'c1', 78.00), ('s2', 'c2', 82.00), ('s2', 'c5', NULL), ('s2', 'c7', 65.00), ('s3', 'c1', 72.00), ('s3', 'c2', 88.00), ('s4', 'c1', 93.00), ('s4', 'c4', NULL), ('s5', 'c4', 68.00), ('s5', 'c6', 75.00), ('s6', 'c4', 82.00), ('s7', 'c7', 100.00), ('s8', 'c3', 96.00);
⚠ 注意两条 NULL 值记录 — s2选了c5但没考试,s4选了c4但没考试。后面练空值查询会用到。
6
创建教师授课表 tc 并插入数据
必做

教师授课表包含:教师号(tno)、课程号(cno)、开课日期(tcdate)。

SQL — 建表
CREATE TABLE tc ( tno VARCHAR(10), cno VARCHAR(10), tcdate DATE, PRIMARY KEY (tno, cno) );
SQL — 插入数据
INSERT INTO tc VALUES ('t1', 'c1', '2021-09-01'), ('t1', 'c4', '2021-03-01'), ('t2', 'c3', '2022-02-20'), ('t2', 'c7', '2021-09-01'), ('t3', 'c2', '2021-09-01'), ('t3', 'c5', '2020-09-01'), ('t4', 'c8', '2021-03-01'), ('t5', 'c1', '2022-09-01'), ('t5', 'c6', '2021-09-01');
✅ 数据准备完成!下面可以运行 SHOW TABLES; 确认 5 张表都已存在。
阶段三

无条件查询练习

对应知识点:SELECT…FROM(投影查询)、别名 AS、DISTINCT、LIMIT

7
例 6-1:查询课程表全部内容(两种写法)
练习

目标:分别用列出字段名和 * 两种方式查询课程表 c 的全部内容,对比结果。

写法一:列出全部字段名
SELECT cno, cn, ct FROM c;
写法二:使用 *
SELECT * FROM c;
✓ 期望结果(8 行,两种写法结果相同)
cnocnct
c1Java程序设计40
c2程序设计基础48
c3线性代数48
c4数据结构64
c5数据库系统56
c6数据挖掘32
c7高等数学60
c8控制理论32
8
AS 别名:让字段名显示为中文
练习

目标:查询课程表,让结果列名显示为"课程号""课程名""课时"。

SQL
SELECT cno AS 课程号, cn AS 课程名, ct AS 课时 FROM c;
💡 对比上一步的结果 — 数据相同,但列标题变成了中文别名。
9
例 6-2:DISTINCT 消除重复行
练习

目标:查询讲授课程的教师号(不重复)。先不加 DISTINCT 看看重复,再加 DISTINCT 对比。

SQL — 不加 DISTINCT(有重复)
-- 先看看不加 DISTINCT 的结果 SELECT tno FROM tc;
SQL — 加 DISTINCT(去重)
-- 再加 DISTINCT 对比 SELECT DISTINCT tno FROM tc;
✓ 加 DISTINCT 后的期望结果(5 行)
tno
t1
t2
t3
t4
t5
⚠ 对比两次查询:不加 DISTINCT 返回 9 行(t1、t2 等出现多次),加了 DISTINCT 只返回 5 行。
10
例 6-3:LIMIT + 自定义字段顺序
练习

目标:查询前 3 位学生的姓名、学号和专业。注意字段顺序是 sn → sno → maj。

SQL
SELECT sn, sno, maj FROM s LIMIT 3;
✓ 期望结果(3 行,注意 sn 在 sno 前面)
snsnomaj
王彤s1计算机
苏乐s2信息
林欣怡s3信息
阶段四

条件查询练习

对应知识点:比较大小、AND/OR/NOT、BETWEEN AND、IN、LIKE、IS NULL

11
例 6-4:比较大小 — 成绩 ≥ 90
练习
SQL
SELECT * FROM sc WHERE score >= 90;
✓ 期望结果(4 行)
snocnoscore
s1c190.50
s4c193.00
s7c7100.00
s8c396.00
12
例 6-5:字符串比较 — 职称 = '教授'
练习
SQL
SELECT tno, tn, maj FROM t WHERE prof = '教授';
✓ 期望结果(2 行)
tnotnmaj
t1刘杨计算机
t4赵礼自动化
💡 字符串值要用单引号括起来:'教授'
13
例 6-6:OR — 专业为"计算机"或"数学"
练习
SQL
SELECT * FROM s WHERE maj = '计算机' OR maj = '数学';
✓ 期望结果(4 行)
snosnsexagemajdept
s1王彤18计算机信息学院
s5魏立17数学理学院
s6何欣荣21计算机信息学院
s7赵琳琳19数学理学院
14
例 6-7 / 6-8:AND / NOT — 年龄范围
练习

先查年龄在 [30,40] 的教师:

SQL — 例 6-7
SELECT tno AS 教师号, tn AS 姓名, prof AS 职称 FROM t WHERE age >= 30 AND age <= 40;

再查年龄不在 [30,40] 的教师(两种等价写法):

SQL — 例 6-8 写法一(NOT)
SELECT tno AS 教师号, tn AS 姓名, prof AS 职称 FROM t WHERE NOT(age >= 30 AND age <= 40);
SQL — 例 6-8 写法二(等价)
SELECT tno AS 教师号, tn AS 姓名, prof AS 职称 FROM t WHERE age < 30 OR age > 40;
💡 两种"取反"写法的结果完全相同,对比理解 NOT 与德摩根定律。
15
例 6-9:括号改变优先级
练习

目标:课程号 c1 或 c2,且开课日期 ≥ 2021-09-01。先加括号运行,再去掉括号对比差异。

SQL — 正确(有括号)
SELECT tno, cno, tcdate FROM tc WHERE (cno = 'c1' OR cno = 'c2') AND tcdate >= '2021-09-01';
SQL — 对比(去掉括号,结果不同!)
-- 去掉括号后 AND 先绑定 cno='c2',含义变了 SELECT tno, cno, tcdate FROM tc WHERE cno = 'c1' OR cno = 'c2' AND tcdate >= '2021-09-01';
❗ 对比两次查询结果的行数差异——理解 AND 优先级高于 OR 为什么需要括号。
16
例 6-10 / 6-11:BETWEEN AND — 范围查询
练习
SQL — 课时在 [30,40]
SELECT cno, cn, ct FROM c WHERE ct BETWEEN 30 AND 40;
SQL — 课时不在 [30,40]
SELECT * FROM c WHERE ct NOT BETWEEN 30 AND 40;
💡 BETWEEN 30 AND 40 是闭区间,包含 30 和 40。NOT BETWEEN 则排除此区间。
17
例 6-12 / 6-13:IN / NOT IN — 集合查询
练习
SQL — IN(课程号为 c4 或 c6)
SELECT sno, cno, score FROM sc WHERE cno IN ('c4', 'c6');
SQL — NOT IN(排除 c4 和 c6)
SELECT sno, cno, score FROM sc WHERE cno NOT IN ('c4', 'c6');
⚠ NOT IN 的等价形式用 AND(不是 OR):cno <> 'c4' AND cno <> 'c6'
18
例 6-14 ~ 6-17:LIKE 模糊查询(% 和 _)
练习

逐个练习以下 4 条语句,观察通配符的不同效果:

例 6-14:包含"程序"
SELECT cno AS 课程号, cn AS 课程名, ct AS 课时 FROM c WHERE cn LIKE '%程序%';
例 6-15:以"程序"开头
SELECT cno AS 课程号, cn AS 课程名, ct AS 课时 FROM c WHERE cn LIKE '程序%';
例 6-16:不以"数据"开头
SELECT * FROM c WHERE cn NOT LIKE '数据%';
例 6-17:第二个字符是"据"(使用 _)
SELECT * FROM c WHERE cn LIKE '_据%';
✅ 对比:%程序% 匹配到 2 个(Java程序设计、程序设计基础),程序% 只匹配 1 个(程序设计基础)。
19
例 6-18 / 6-19:IS NULL / IS NOT NULL
练习

目标:分别查询没有成绩和有成绩的选课记录,理解 NULL 的特殊性。

SQL — 没有成绩(NULL)
SELECT sno, cno FROM sc WHERE score IS NULL;
SQL — 有成绩(非 NULL)
SELECT sno, cno FROM sc WHERE score IS NOT NULL;
⚠ 错误写法(试一试,体会为什么不行)
-- 这样写查不到任何结果! SELECT sno, cno FROM sc WHERE score = NULL;
❗ 最后一条语句返回空结果!因为 NULL 不等于任何值(包括它自己),只能用 IS NULL 判断。这是初学者最常见的错误。
自测挑战

独立完成 — 不看答案试试

以下题目基于上面的 5 张表,先自己写 SQL 再点击查看参考答案

🏋️ 挑战 1:查询学院为"信息学院"的所有学生的姓名和专业。
参考答案
SELECT sn, maj FROM s WHERE dept = '信息学院';
🏋️ 挑战 2:查询成绩在 [70, 85] 之间的选课记录(用 BETWEEN AND)。
参考答案
SELECT * FROM sc WHERE score BETWEEN 70 AND 85;
🏋️ 挑战 3:查询课程名中包含"数"字的所有课程的课程号和课程名。
参考答案
SELECT cno, cn FROM c WHERE cn LIKE '%数%';
🏋️ 挑战 4:查询年龄小于 30 岁的讲师(职称='讲师'),显示教师号、姓名、年龄。
参考答案
SELECT tno, tn, age FROM t WHERE age < 30 AND prof = '讲师';
🏋️ 挑战 5:查询选了 c1 但成绩不到 80 的学生学号和成绩(综合条件)。
参考答案
SELECT sno, score FROM sc WHERE cno = 'c1' AND score < 80;
🏋️ 挑战 6:查询专业不是"计算机""信息""数学"的学生信息(用 NOT IN)。
参考答案
SELECT * FROM s WHERE maj NOT IN ('计算机', '信息', '数学');
🎉

本次练习完成!

你已完成查询结构、无条件查询和条件查询的全部上机练习。
下节课将继续练习聚合函数、分组查询、排序和 LIMIT。