ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MySQL实训答案知识地图:从建表到存储过程的核心考点与易错点

MySQL实训答案知识地图:从建表到存储过程的核心考点与易错点

简介:这份PDF面向正在学习或备考MySQL数据库实训的学生与初学者,针对头歌平台实训题目提供可直接对照的参考答案,帮助解决建库建表、约束设置与表结构修改等练习中的卡点。资源为单个PDF文件,约433KB,正文按实训关卡组织并配有目录,便于按知识点快速定位。内容覆盖数据库定义与操作、主键与外键及唯一约束的应用、查看表结构与修改表名、修改字段名与数据类型、添加删除字段、调整字段排列位置、删除外键约束,以及插入与更新数据等典型关卡,每关均给出完整SQL语句与执行环境提示。已有18400人学习下载,适合课堂实训跟练、作业自查与考前复习时逐条比对,也可作为教师核对参考答案的辅助材料。

1. 从一份实训答案反推 MySQL 知识地图

很多人拿到一份 MySQL 实训答案,第一反应是照着抄、跑通就完事。但真正复习过一轮的人会发现,这类实训答案其实是一张被拆成关卡的知识地图:从建库建表、约束设计,到单表查询、连接查询、子查询,再到视图、索引、存储过程,几乎把 MySQL 入门到进阶的主干全串了一遍。这份资料按「数据定义与操作 → 表的基本操作 → 单表查询 → 连接与子查询 → 复杂查询 → 聚合与函数 → 视图索引 → 存储过程」组织,带目录、按关卡给代码,适合正在系统学 MySQL、或者准备数据库实训考核的人拿来对照复习。下面按模块把重点、难点和易错点梳理清楚,让你从「抄答案」变成「看得懂为什么这么写」。

2. 数据定义与约束:建库建表的地基怎么打

数据定义是整个 MySQL 实训的第一段,也是后面所有查询的前提。这一块看起来只是CREATE DATABASE和CREATE TABLE,但真正拉开差距的是约束设计——主键、外键、唯一、默认值、自增,每一个都对应着「数据能不能乱写」的规则。实训答案里第 1 关到第 5 关正好把这条线走完:先建库,再建表,然后逐层加约束。

2.1 建库建表与常用数据类型

建库的标准动作是先连上服务,再CREATE DATABASE,然后USE切进去。实训里反复出现的连接命令是mysql -uroot -p123123 -h127.0.0.1,这条命令拆开看就是用户名、密码、主机地址三要素,本地练习时主机可以省略,但写全了更接近真实环境。

-- 连接后先建库,再切换,避免建表建到错误的库里 CREATE DATABASE MyDb; USE MyDb; -- 建表时把类型、长度一次写清楚 CREATE TABLE t_emp ( id INT, name VARCHAR(32), deptId INT, salary FLOAT );

这里最容易忽略的是「先 USE 再建表」。很多人建完库直接CREATE TABLE,结果表落到了默认库里,后面DESCRIBE找不到表就开始怀疑人生。类型选择上,INT用于整数、VARCHAR(n)用于变长字符串、FLOAT用于小数,实训里的表结构基本就围绕这几类展开,先把这套组合用熟,后面加约束才不会乱。

2.2 主键、外键、唯一与默认约束

约束是数据定义部分的核心考点,实训答案里用四张表把常见约束过了一遍。主键分单列主键和复合主键,单列主键直接写在字段后,复合主键要单独写一行PRIMARY KEY(name, phone)。外键则要先用CONSTRAINT起名,再FOREIGN KEY ... REFERENCES指向父表。

约束类型写法要点作用
主键 PRIMARY KEY单列写在字段后,复合单独一行唯一标识一行,非空且唯一
外键 FOREIGN KEYCONSTRAINT 名 FOREIGN KEY(列) REFERENCES 父表(列)保证引用完整性
唯一 UNIQUE字段后加UNIQUE列值不重复,可为空
默认 DEFAULT字段后加DEFAULT 值未赋值时填默认值
自增 AUTO_INCREMENT配合主键使用自动生成递增整数
-- 复合主键:两个字段一起唯一 CREATE TABLE t_user2 ( name VARCHAR(32), phone VARCHAR(11), email VARCHAR(32), PRIMARY KEY(name, phone) ); -- 外键:先给约束起名,方便以后删除 CREATE TABLE t_student ( id INT PRIMARY KEY, name VARCHAR(22), classId INT, CONSTRAINT fk_stu_class1 FOREIGN KEY(classId) REFERENCES t_class(id) ); -- 自增 + 非空 + 唯一 + 默认值一起上 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE, sex VARCHAR(4) DEFAULT '男' ) DEFAULT CHARSET=utf8;

梳理下来会发现,约束的顺序有讲究:主键通常最先定,因为外键要引用父表主键;自增只能配主键或唯一键;默认值和字符集放在表级更稳妥。实训里DEFAULT CHARSET=utf8写在建表语句末尾,就是为了避免中文乱码这个老问题。

2.3 表结构的查看与修改

建完表不等于结束,实训第二段专门练ALTER TABLE。改表名用RENAME,改字段名用CHANGE(要同时写旧名新名和类型),只改类型用MODIFY,加字段用ADD ... AFTER,删字段用DROP,调位置用FIRST或AFTER。

-- 改表名 ALTER TABLE tb_emp RENAME jd_emp; -- 改字段名(CHANGE 必须带新类型) ALTER TABLE tb_emp CHANGE Id prod_id INT(11); -- 只改类型 ALTER TABLE tb_emp MODIFY Name VARCHAR(30); -- 加字段并指定位置 ALTER TABLE tb_emp ADD Country VARCHAR(20) AFTER Name; -- 删字段 ALTER TABLE tb_emp DROP Salary; -- 调字段到最前 ALTER TABLE tb_emp MODIFY Name VARCHAR(25) FIRST;

CHANGE和MODIFY是这一节最容易混的一对:CHANGE能改名也能改类型,MODIFY只能改类型不能改名。另外删除外键要用DROP FOREIGN KEY 约束名,不是删字段,删完可以用SHOW CREATE TABLE ... \G验证。

提示:ALTER TABLE改结构前先DESCRIBE看一眼现状,改完再DESCRIBE确认,两步都别省。

3. 单表查询:WHERE、排序、分组与分页的主线

单表查询是实训里关卡最多的一段,从基本SELECT一路练到LIKE、IS NULL、DISTINCT、ORDER BY、GROUP BY、LIMIT。这条主线其实就是 SQL 的执行顺序:先FROM定表,再WHERE过滤,然后GROUP BY分组、HAVING过滤组,接着ORDER BY排序,最后LIMIT截取。把顺序记牢,写查询时就不会把条件放错地方。

3.1 条件过滤:IN、BETWEEN、LIKE 与空值

条件过滤的关键是选对运算符。IN用于枚举匹配,BETWEEN AND用于闭区间,LIKE配合%做模糊匹配,空值必须用IS NULL而不能用= NULL。

-- IN:排除某个 id SELECT Name, Salary FROM tb_emp WHERE Id NOT IN (1); -- BETWEEN AND:闭区间,含两端 SELECT Name, Salary FROM tb_emp WHERE Salary BETWEEN 3000 AND 5000; -- LIKE:C 开头的名字 SELECT Name, Salary FROM tb_emp WHERE Name LIKE 'C%'; -- 空值判断:不能用 = NULL SELECT * FROM tb_emp WHERE DeptId IS NULL; -- 去重 SELECT DISTINCT Name FROM tb_emp;

这里有个血泪经验:= NULL永远返回空结果,因为 NULL 不等于任何值,包括它自己。实训里专门设了一关练IS NULL,就是为了把这个坑提前踩掉。LIKE的%代表任意长度,_代表单个字符,写C%是 C 开头,写%C%是包含 C,别混。

3.2 排序、分组与 LIMIT 分页

排序用ORDER BY 列 DESC/ASC,分组用GROUP BY,分页用LIMIT 偏移, 条数。实训里「查询第 2 名到第 5 名」就是LIMIT 1,4——偏移从 0 开始,所以第 2 名偏移是 1。

-- 按成绩降序 SELECT * FROM tb_score WHERE class_id = 1 ORDER BY score DESC; -- 按班级分组 SELECT * FROM tb_class GROUP BY class_id; -- 第 2 名到第 5 名:偏移 1,取 4 条 SELECT * FROM tb_score ORDER BY score DESC LIMIT 1, 4;

LIMIT的偏移量是最容易算错的地方:LIMIT 0,5是前 5 条,LIMIT 5,5是第 6 到第 10 条。分页查询在数据量大时还会遇到性能问题,实训后面用「子查询 + 主键比较」来优化深分页,思路是先查出边界主键再取范围,避免大偏移扫描。

3.3 聚合函数与 HAVING 的组合

聚合函数COUNT、SUM、AVG、MAX、MIN是分组统计的标配,配合GROUP BY出分组结果,再用HAVING过滤分组后的条件。WHERE过滤行,HAVING过滤组,这个分工必须分清。

-- 按年级和性别统计人数 SELECT gradeId, sex, COUNT(*) FROM student WHERE gradeId IN (2,3,4) GROUP BY gradeId, sex; -- 至少两门 90 分以上的学生 SELECT sno, COUNT(*) FROM tb_grade WHERE score >= 90 GROUP BY sno HAVING COUNT(pno) >= 2;

HAVING里可以用聚合函数,WHERE里不行,这是两者最本质的区别。实训里「平均分大于 90 且语文 95 以上」那题,就是先用子查询筛出语文 95 以上的学号,再在外层GROUP BY后用HAVING卡平均分,两层条件各归其位。

4. 连接与子查询:多表取数的两条路

单表查询练熟后,实训进入多表阶段。连接查询解决「表与表横向拼」的问题,子查询解决「用一张表的结果去筛另一张表」的问题。这两条路经常混着用,复杂查询关卡里几乎每道题都是连接加子查询的组合。

4.1 内连接、外连接与复合条件连接

内连接只保留两表都匹配的行,外连接保留一侧全部行。左外连接保留左表全部,右外连接保留右表全部。实训里用JOIN ... ON和LEFT/RIGHT JOIN两种写法都练过。

-- 内连接:学生和班级 SELECT s.name AS studentName, c.name AS className FROM tb_student s JOIN tb_class c ON c.id = s.class_id; -- 左外连接:保留所有学生 SELECT s.name AS studentName, c.name AS className FROM tb_student s LEFT JOIN tb_class c ON c.id = s.class_id; -- 复合条件连接:分数 90 以上 SELECT s1.name AS studentName, score, s2.name AS className FROM tb_student s1, tb_class s2 WHERE s1.class_id = s2.id AND s1.score > 90 ORDER BY score DESC;

外连接的方向是最容易翻车的地方:A LEFT JOIN B保留 A 的全部行,A RIGHT JOIN B保留 B 的全部行。实训答案里有一处用tb_class RIGHT JOIN tb_student来实现「所有学生」,本质等价于tb_student LEFT JOIN tb_class,方向反了结果就少人。写外连接前先问自己「要保留哪张表的全部行」,答案就清楚了。

4.2 子查询:比较、ANY/ALL 与 IN

子查询是嵌在另一条语句里的SELECT,按返回结果分标量子查询、列子查询、行子查询。实训里重点练了比较运算符子查询、ANY/ALL和IN。

-- 比较运算符:大于平均年龄 SELECT name, age FROM tb_emp WHERE age > (SELECT AVG(age) FROM tb_emp); -- ANY:大于 java 岗位最低工资的任意一个 SELECT position, salary FROM tb_salary WHERE salary > ANY (SELECT MIN(salary) FROM tb_salary WHERE position = 'java'); -- ALL:大于 java 岗位所有工资 SELECT position, salary FROM tb_salary WHERE salary > ALL (SELECT salary FROM tb_salary WHERE position = 'java'); -- IN:岗位是 java SELECT position, salary FROM tb_salary WHERE position IN ('java');

ANY和ALL的区别记一句话:> ANY等价于大于最小值,> ALL等价于大于最大值。实训答案里有一处注释写「使用 ALL 关键字」但实际写的是ANY,这种答案和注释对不上的情况在整理资料里并不少见,复习时以语义为准,别被注释带偏。

4.3 复杂查询里的排名、换位与统计

复杂查询关卡把前面所有技能揉在一起:交换工资用CASE WHEN,换座位用IF加奇偶判断,分数排名用相关子查询数「比自己高的有几个」,体育馆人流量用三表自连接找连续三天。

-- 交换性别:CASE 表达式 UPDATE tb_Salary SET sex = CASE sex WHEN 'm' THEN 'f' ELSE 'm' END; -- 换座位:偶数减一,奇数加一,末位不动 SELECT IF(Id % 2 = 0, Id - 1, IF(Id = 5, Id, Id + 1)) AS id, name FROM tb_Seat ORDER BY Id; -- 分数排名:比自己高的不同分数个数 + 1 SELECT Score, (SELECT COUNT(DISTINCT score) FROM score WHERE score >= s.score) AS Rank FROM score s ORDER BY Score DESC;

排名题有两种写法:COUNT(DISTINCT score)处理并列名次,COUNT(*) + 1处理连续名次,题目要哪种看它是否允许并列。体育馆那题用三表自连接加OR条件判断连续 id,本质是在没有窗口函数的 MySQL 版本里模拟「连续区间」检测,思路值得记下来。

5. 视图、索引与存储过程:进阶模块的取舍

实训后半段进入开发技巧:视图、索引、分页优化、存储过程。这几块不再是单纯写查询,而是涉及「怎么组织数据访问」的设计问题。视图简化复杂查询,索引加速检索,存储过程封装逻辑,三者各有适用场景,也各有代价。

5.1 视图:把复杂查询存成一张虚拟表

视图是基于查询结果的虚拟表,创建用CREATE VIEW 名 AS 查询。单表视图和多表视图的区别只在查询本身,视图不存数据,每次查询都重新执行底层语句。

-- 单表视图:数学加语文总分 CREATE VIEW stu_view AS SELECT math, chinese, math + chinese FROM student; -- 多表视图:学生和班级信息 CREATE VIEW stu_classes AS SELECT student.stu_id, student.name, stu_info.classes FROM student, stu_info WHERE student.stu_id = stu_info.stu_id;

视图的价值在于把多表连接和计算逻辑封装起来,调用方只写SELECT * FROM stu_view。但视图也有代价:底层查询复杂时每次访问都要重算,且部分视图不可更新。复习时记住「视图是逻辑封装,不是性能优化手段」就够了。

5.2 索引:单列、唯一与组合索引

索引是加速查询的核心手段。主键自带索引,唯一索引保证列值唯一,普通索引只加速不约束,组合索引按最左前缀匹配。

-- 建表时主键索引 CREATE TABLE student ( id INT(11) NOT NULL AUTO_INCREMENT, name VARCHAR(20) NOT NULL, score INT(10), PRIMARY KEY (id) ); -- 唯一索引 CREATE UNIQUE INDEX name_index ON student(name); -- 普通索引 CREATE INDEX score_index ON student(score); -- 组合索引 ALTER TABLE person ADD INDEX name_city_score (name, age, address); -- 查看索引 SHOW INDEX FROM student;

组合索引的关键是最左前缀:(name, age, address)能命中name、name+age、name+age+address,但单独查age用不上。实训里专门练了组合索引的创建,复习时要把「最左前缀」和「索引失效场景」一起记,比如对索引列做函数运算、用LIKE '%x'前置通配,都会让索引白建。

5.3 存储过程:IN/OUT 参数与流程控制

存储过程把一段 SQL 逻辑存到数据库里,用CREATE PROCEDURE定义,CALL调用。参数分IN(传入)、OUT(传出)、INOUT(双向)。流程控制有IF...ELSEIF...ELSE、CASE、循环。

DROP PROCEDURE IF EXISTS GetCustomerLevel; DELIMITER $$ CREATE PROCEDURE GetCustomerLevel( IN p_customNumber INT(11), OUT p_customerLevel VARCHAR(10) ) BEGIN DECLARE levels INT; SELECT creditlimit INTO levels FROM customers WHERE customerNumber = p_customNumber; IF levels < 5000 THEN SET p_customerLevel = 'SILVER'; ELSEIF levels < 10000 THEN SET p_customerLevel = 'GOLD'; ELSE SET p_customerLevel = 'PLATINUM'; END IF; END $$ DELIMITER ;

存储过程最容易错的是分隔符:过程体里有分号,不先用DELIMITER $$改掉默认分隔符,MySQL 会在第一个分号处就截断。另外DECLARE必须写在BEGIN之后、其他语句之前,顺序错了直接报语法错误。

6. 易错点排查:这几处最容易翻车

把整份实训答案过一遍,会发现错误高度集中在几个地方。下面按「易漏点 → 为什么漏 → 怎么补」列出来,复习时对着自查。

易漏点一:= NULL当空值判断。为什么漏:直觉上「等于空」很自然,但 SQL 里 NULL 表示未知,任何与 NULL 的比较都返回未知。怎么补:空值一律用IS NULL/IS NOT NULL,写完条件扫一眼有没有= NULL。

易漏点二:CHANGE和MODIFY混用。为什么漏:两者都能改字段,名字又像。怎么补:记「CHANGE 要写两遍字段名(旧新),MODIFY 只写一遍」,改名前先DESCRIBE确认旧名。

易漏点三:外连接方向搞反。为什么漏:LEFT/RIGHT保留哪边全靠记,一急就反。怎么补:写之前先明确「要保留哪张表全部行」,那张表放LEFT JOIN左边,或者用RIGHT JOIN时把它放右边。

易漏点四:WHERE和HAVING放错聚合条件。为什么漏:两个都是过滤,位置又近。怎么补:聚合函数条件进HAVING,普通列条件进WHERE,HAVING永远跟在GROUP BY后面。

易漏点五:存储过程忘了改分隔符。为什么漏:过程体里全是分号,默认分隔符会提前截断。怎么补:CREATE PROCEDURE前先DELIMITER $$,结束后DELIMITER ;改回来,养成固定动作。

注意:实训答案里个别关卡的注释和实际代码不一致(比如注释写 ALL、代码写 ANY),复习时以语句语义为准,别照抄注释。

7. 复习清单:从抄答案到能默写

把这份资料当复习材料,最有效的用法不是从头抄到尾,而是按「能默写」的标准过一遍。下面这张清单按模块列了必须能独立写出的语句,每条都对应实训里的关卡,写完对照答案查漏。

模块必须能默写自查标准
建库建表CREATE DATABASE、CREATE TABLE带类型不看答案写出含 4 字段的表
约束主键、复合主键、外键、唯一、默认、自增说清每种约束写在哪一行
改表RENAME、CHANGE、MODIFY、ADD、DROP区分 CHANGE 与 MODIFY
单表查询WHERE、IN、BETWEEN、LIKE、IS NULL空值不用= NULL
排序分组ORDER BY、GROUP BY、HAVING、LIMITLIMIT 偏移算对
连接JOIN ON、LEFT/RIGHT JOIN外连接方向不反
子查询比较、ANY/ALL、IN说清 ANY 与 ALL 差别
聚合COUNT、SUM、AVG、MAX、MIN配合 GROUP BY 使用
视图索引CREATE VIEW、CREATE INDEX、组合索引记住最左前缀
存储过程DELIMITER、IN/OUT、IF分隔符先改后还原

具体操作上,我自己的习惯是分三轮:第一轮按关卡顺序把答案跑一遍,确认环境能通;第二轮遮住答案,只看题目描述默写,写不出来的标记;第三轮专攻标记题,把易错点那五条对着改一遍。默写时特别注意LIMIT偏移、外连接方向、HAVING位置这三处,它们是最容易在考场上手滑的地方。

从那以后我每次复习数据库,都强制自己先默写再对答案,而不是先看答案找感觉——先看答案会产生「我会了」的错觉,默写才会暴露真正的漏洞。这份带目录的实训答案正好适合当默写题库,按模块过一遍,比零散刷题扎实得多。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进