ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

MySQL实训答案速查:从建表到查询的避坑指南

MySQL实训答案速查:从建表到查询的避坑指南 简介这是一份针对头歌平台MySQL数据库实训的全流程答案整理面向正在完成数据库定义、表操作、约束应用等关卡的学习者也适合需要快速巩固数据库基础、备战实训考试的高校学生。内容按实训模块逐关编排既有创建数据库、创建数据表等入门操作也覆盖主键、外键、唯一、默认等约束的设置以及查看表结构、修改表名、调整字段类型与位置、增删字段、插入更新数据等高频场景每步配有可直接执行的数据库语句方便对照验证、快速定位错误。正文以代码块形式组织命令前均标注对应关卡遇到报错时可逐条核对减少试错成本。资源包共包含一个PDF格式文件大小约四百三十三KB文件带有清晰目录可依据关卡编号直接跳转查阅。目前已有一万八千三百六十九人学习下载对初学者和需要高效完成实训任务的学习者都有较强的参考价值。1. MySQL实训答案一份能跑通但别盲抄的速查手册MySQL 数据库实训向来不缺题目缺的是能一次跑通的答案。这份《头歌MySQL数据库实训答案》PDF 把建库、建表、约束、增删改查、连接查询、子查询、聚合函数、视图、索引、存储过程按关卡拆成了可直接粘贴的 SQL 片段适合正在赶实训进度、又不想在环境配置上耗时间的从业者和学生。但我的建议是答案可以抄抄完必须做两件事——理解每条语句的边界条件再在本地用同样的表结构重跑一遍。这份资源解决的是「卡在第几步、语法怎么拼、表名哪里搞错」这类具体问题而不是替你学会 SQL。如果你只是想要一份按目录检索的参考手册它比翻文档快得多如果你指望粘贴完就彻底掌握那大概率会在第二个项目里翻车。2. 建库建表与约束把 DDL 练成肌肉记忆的三类写法2.1 最小可跑通的建库建表从零到 t_emp实训里的每个新项目几乎都以这三步开场登录、建库、建表。资源里这条命令序列是整套答案里出现频率最高的骨架。# 登录实训沙箱 MySQL 服务-u 指定用户-p 带出密码-h 指定主机 mysql -uroot -p123123 -h127.0.0.1use MyDb; # 切换到 MyDb 数据库后续建表都建在这个库里 create table t_emp ( # 建员工表四个字段按题目顺序排列 id int, # 整型员工编号 name varchar(32), # 变长字符串最多存 32 个字符 deptId int, # 部门编号 salary float # 浮点型工资 );第一段是命令行登录参数-u root指定用户名为 root-p123123是密码直接跟在 p 后面中间不留空格-h127.0.0.1表示连本机。实训沙箱里这套参数是固定的本地环境直接换密码即可。第二段是建表语句varchar(32)不是固定占 32 字节而是最多存 32 个字符超过会截断salary float用浮点存工资实训里够用但真实业务我一般用decimal(10,2)避免精度漂移。建表后第一时间执行desc t_emp;或者show create table t_emp;看字段顺序和类型。实训评分经常比对查询结果的列顺序id, name, deptId, salary和你写成id, salary, name, deptId在后面的查询关卡里可能结果内容一样但列头对不上评分脚本直接判定失败。2.2 主键与外键单列、复合和命名约束的选择主键约束在这份资源里出现了三次不同写法分别对应单列主键、复合主键和外键引用。先看前两种# 单列主键直接在列定义后面声明 PRIMARY KEY create table t_user1( userId INT PRIMARY KEY, # 用户编号作为唯一标识 name VARCHAR(32), password VARCHAR(11), phone VARCHAR(11), email VARCHAR(32) ); # 复合主键把 name 和 phone 组合成一个整体唯一标识 create table t_user2( name VARCHAR(32), phone VARCHAR(11), email VARCHAR(32), PRIMARY KEY(name, phone) # 两列组合值必须唯一 );单列主键适合一张表里已经有一个天然唯一字段的场景比如用户编号。复合主键则用在单列都不唯一、但两列组合才能唯一的情况。注意复合主键的判定规则name单独可以重复phone单独也可以重复但(name, phone)这对组合不能重复。实训里检查你写得对不对用desc看 Key 列PRI 出现在哪一行、出现几次立刻就知道是单列还是复合。外键约束才是这一节的重点资源里给的标准写法长这样# 先建父表 t_class CREATE TABLE t_class( id INT PRIMARY KEY, name VARCHAR(22) ); # 再建子表 t_studentclassId 引用 t_class 的 id CREATE TABLE t_student( id INT PRIMARY KEY, name VARCHAR(22), classId int, CONSTRAINT fk_stu_class1 FOREIGN KEY(classId) REFERENCES t_class(id) );这里CONSTRAINT fk_stu_class1是给外键起名后面FOREIGN KEY(classId) REFERENCES t_class(id)声明子表 classId 引用父表 id。实训环境里最常翻车的点有两个一是执行顺序颠倒必须先建父表再建子表反过来直接报「无法创建外键约束」二是两侧字段类型必须完全一致t_class.id是 INTt_student.classId也必须是 INT写成 VARCHAR 或漏掉类型都会失败。2.3 添加常用约束非空、唯一、默认值与字符集这一关的建表语句把实训要求的大部分约束一次集齐了CREATE TABLE t_user( id INT PRIMARY KEY AUTO_INCREMENT, # 主键自增插入时不需手动给id username VARCHAR(32) NOT NULL UNIQUE, # 用户名非空且唯一 sex VARCHAR(4) DEFAULT 男 # 不传 sex 时默认填“男” ) DEFAULT CHARSETutf8; # 表级字符集中文不乱码AUTO_INCREMENT让主键从 1 开始自动累加插入时不需要写 id 字段。NOT NULL UNIQUE同时限制了非空和唯一两项规则只要有一条记录插入了 NULL 或重复的 username整条 INSERT 直接报错。DEFAULT 男是列级默认值INSERT语句没给 sex 字段时自动补上。注意DEFAULT CHARSETutf8这一句经常被忽略。实训平台大多数默认 utf8mb4显式写 utf8 也能过但你本地导数据到其他库时字符集不一致会导致中文变成问号。我的习惯是建完表立刻执行show create table t_user;重点看三处ENGINE 是不是 InnoDBCHARSET 是不是 utf8id 的 Extra 列是不是 auto_increment。这三个属性决定了后续外键、自增、中文查询能不能正常跑。2.4 表结构维护ALTER TABLE 五类高频操作项目进行到一半发现表结构不对就要靠ALTER TABLE补救。资源里覆盖了改名、改字段名、改类型、增删列、移动位置我把这几条命令理到一起ALTER TABLE tb_emp RENAME jd_emp; # 改表名 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 DeptId int(11) AFTER Salary; # 移动列位置CHANGE和MODIFY的差别是这组命令里最值得记的对比点语句能改列名能改类型适用场景CHANGE可以可以列改名加类型调整MODIFY不可以可以只改类型或移动位置CHANGE后面必须带着完整的列定义老名字和新名字都要写int(11)不能省MODIFY只需写列名加新类型。ADD ... AFTER Name把新列插到 Name 后面FIRST则把列移到第一个位置。DROP Salary删除列没有后悔药实训沙箱里删完想恢复只能重新建表再插数据。提示多条 ALTER 语句不要挤在一个执行框里连写。实训评分按行解析漏一个分号会把后面的语句当成上一条的从句报语法错误却不告诉你错在哪一行。我一般每条 ALTER 单独执行确认DESC结果符合预期再写下一条。3. 数据操作与单表查询增删改查的四个边界条件3.1 批量插入一条 INSERT 写多行表结构就绪后的第一件事是造数据。资源里的批量插入示例非常典型# 一次插入三行员工记录字段顺序与表结构保持一致 INSERT INTO tb_emp(Id,Name,DeptId,Salary) VALUES (1,Nancy,301,2300.00), (2,Tod,303,5600.00), (3,Carly,301,3200.00);一条 INSERT 后跟多组 VALUES每组用括号包住、逗号分隔这是标准的批量写入写法。字段列表(Id,Name,DeptId,Salary)限定了每组值的顺序多写或少写一个值都会报「Column count doesnt match value count」。评分会统计表内的总行数少插一条就少拿一分所以提交前用select count(*) from tb_emp;对比一下。这里有个容易被忽视的坑字符串用双引号还是单引号。MySQL 默认两种都能识别但实训平台开启了 ANSI_QUOTES 模式时双引号会被当成标识符而不是字符串同样的语句直接报错。稳妥做法是统一用单引号这也是 SQL 标准推荐写法。3.2 更新与删除WHERE 条件决定影响范围数据插进去之后实训马上会考改数据。资源里这两条语句看似简单边界条件却很值得拆开看# 只更新 id3 的那一行SET 可以同时改多个字段 UPDATE tb_emp SET NameTracy, DeptId302, Salary4300.00 WHERE id3; # 删除工资大于 3000 的所有记录 DELETE FROM tb_emp WHERE Salary3000;UPDATE 的三个赋值之间用逗号分隔最后用 WHERE 限定行。这里如果把 WHERE 漏掉整张表所有行都会被改成同一组值在实训里可能显示不出来但数据全毁了。DELETE 同样依赖 WHERE 控制删除范围Salary3000删掉高工资Salary0则等于清空全表。真实业务里一条不带 WHERE 的 DELETE 甚至可能触发数据库的批量删除保护机制直接拒绝执行。我的操作习惯是写 UPDATE 或 DELETE 前先跑一遍同样 WHERE 条件的 SELECT-- 先看会命中哪些行 SELECT * FROM tb_emp WHERE Salary3000;确认结果里就是要删的那几条再执行 DELETE。这个习惯在实训里能帮你提前发现条件写反在真实项目里能避免删错数据。3.3 单表查询IN、BETWEEN、LIKE、IS NULL 的判定细节查询语句是实训占比最大的一块。资源里四个关键字查询放在一起刚好覆盖最常见的判定边界# NOT INid 不是 1 的所有员工 SELECT Name,Salary FROM tb_emp WHERE Id NOT IN (1); # BETWEEN 是闭区间包含 3000 和 5000 两条边界 SELECT Name,Salary FROM tb_emp WHERE Salary BETWEEN 3000 AND 5000; # LIKE 的 % 是任意长度通配符C% 表示以 C 开头 SELECT Name,Salary FROM tb_emp WHERE Name LIKE C%; # NULL 只能 IS NULL 判断写成 NULL 永远查不到数据 SELECT * FROM tb_emp WHERE DeptId IS NULL;IN的列表里写的是 id1NOT IN取反返回所有 id 不是 1 的行。BETWEEN 3000 AND 5000是闭区间等价于3000 AND 5000。LIKE C%只匹配以 C 开头的名字%C%表示包含 CC_表示 C 后面正好一个字符。IS NULL专门查空值新手最常见的错误是写DeptId NULL这个表达式的结果永远是 NULL不会返回任何行必须用IS NULL或IS NOT NULL。提示SQL 里 NULL 不是值而是「未定义」。任何 NULL 参与的等值比较结果都不是 true也不是 false而是未知。这就是为什么NULL查不出东西。去重也是一个高频考点# DISTINCT 作用在整行而不是单个列 SELECT DISTINCT Name FROM tb_emp;DISTINCT如果写成SELECT DISTINCT Name, DeptId去重的是两列的组合。只有当你希望基于两列组合去重复时才这么写否则结果会比你预期的多。3.4 排序、分组与 LIMIT三条语句的配合顺序查询的进阶形态是把排序、分组、限量三个动作组合起来。资源里这三条语句恰好演示了最容易搞混的两处# 按成绩降序排列DESC 从高到低ASC 从低到高 SELECT * FROM tb_score WHERE class_id 1 ORDER BY score DESC; # GROUP BY 之后 SELECT 里最好只保留分组列和聚合函数 SELECT * FROM tb_class GROUP BY class_id; # LIMIT 第一个参数是偏移量0 表示第一行第二个参数是取几行 SELECT * FROM tb_score ORDER BY score DESC LIMIT 1,4;第一条是排序ORDER BY只影响展示顺序不影响返回哪些行多个排序字段用逗号分隔。第二条是分组GROUP BY class_id之后执行SELECT *在 MySQL 5.7 里通常返回每组的第一行但这是未定义行为换了版本结果可能不同。正经写法是SELECT class_id, COUNT(*) FROM tb_class GROUP BY class_id;。第三条的 LIMIT 是所有新手必踩的坑LIMIT 1,4表示从第 2 行开始取 4 行也就是第 2 到第 5 名不是第 1 到第 4 名。LIMIT 的第一个参数是偏移量从 0 开始计数。如果题目要「第 2 名到第 5 名」LIMIT 1,4是对的如果只是「前 4 名」要写LIMIT 4或LIMIT 0,4。4. 连接查询与子查询多表场景的三种查询路径4.1 内连接与外连接谁保留谁决定方向连接查询是实训里拉开差距的关卡。资源里三个题目把内连接、左外连接、右外连接一网打尽# 内连接只返回两边都匹配得上的行 select tb_student.name as studentName, tb_class.name as className from tb_student join tb_class on tb_class.id tb_student.class_id; # 右外连接保留右表 tb_student 全部行 select tb_student.name as studentName, tb_class.name as className from tb_class right join tb_student on tb_class.idtb_student.class_id; # 左外连接保留左表 tb_class 全部行 select tb_student.name as studentName, tb_class.name as className from tb_class left join tb_student on tb_class.idtb_student.class_id;第一段JOIN ... ON是内连接学生表里如果有 class_id 指向不存在的班级这一行会被过滤掉。第二段和第三段的对比很有迷惑性代码只差一个 left/right语义完全相反。题目要求「查询所有学生姓名和对应的班级」意味着必须保留学生表的全部行哪怕某个学生的班级 id 在班级表里不存在。第二段用tb_class RIGHT JOIN tb_student能正确保留右表 tb_student 全部行。第三段用tb_class LEFT JOIN tb_student保留的是班级表全部行没学生的班级也会出现学生字段显示 NULL方向就反了。我的判断方法是先问自己结果里必须出现哪张表的全部行那张表就要放在「保留表」的位置。LEFT JOIN保留左表RIGHT JOIN保留右表。实训题目里描述是「所有学生」时优先考虑tb_student LEFT JOIN tb_class这样语义最直白。4.2 复合条件连接与子查询比较、ALL/ANY、IN多表关联不一定要用 JOIN 关键字老式逗号写法在实训题目里也很常见# 三表关联连接条件放在 WHERE 里过滤条件放在 AND 后面 select s1.name as studentName, score, s2.name as className from tb_student as s1, tb_class as s2 where s1.class_id s2.id and s1.score 90 order by score desc;s1和s2是表别名写长查询时能省大量字符也避免同名字段歧义。这段的等价写法是tb_student join tb_class on tb_student.class_id tb_class.id where score 90。and s1.score90是过滤条件漏掉的话 90 分以下的学生也会混进结果。子查询部分最考验对关键字语义的理解。资源里这组代码是典型代表# 标量子查询内层返回平均年龄外层逐行比较 select name, age from tb_emp where age (select avg(age) from tb_emp); # ANY大于子查询结果里的任意一个值即可 SELECT position,salary FROM tb_salary WHERE salary ANY(SELECT max(salary) FROM tb_salary where positionjava);第一个是标量子查询内层返回一个值外层用比较运算符和它比较逻辑直白。第二个是 ANY 的语义salary ANY(...)只要大于子查询返回集合里的任意一个值就满足。这里子查询只返回一个 max(salary)ANY 和直接大于最大值等价。如果把 ANY 换成 ALL表示要大于所有值而没有任何工资能大于自己的最大值这个条件永远为假。这两种关键字放在一起就是用来考察你分不分得清集合逻辑。IN 子查询在实训里更常用典型场景是「先查出符合条件的 id 集合再查这些 id 的完整记录」# 查出 90 分以上的学生 id外层再取完整信息 select * from student where s_id in ( select s_id from score where s_score 90 );内层select s_id from score where s_score 90返回一批学生编号外层用 IN 判断 s_id 是否落在其中。这个写法比 JOIN 更直观尤其适合一层套一层的统计题目。4.3 聚合函数与分组统计COUNT、SUM、AVG、MAX、MIN 的边界聚合函数这一关是必考项。资源里把五个函数分开练习但实际组合使用更常见。先看基础用法# COUNT(*) 数行数COUNT(列名) 数列里非 NULL 的个数 select count(*) from tb_class; select classid, count(*) from tb_class where classid367; # SUM 和 AVG 自动忽略 NULL但列必须是数值类型 select sum(score) from tb_class; select course, avg(score) from tb_class where course语文; # MAX/MIN 对字符串也有效按字典序取最大最小 select course, max(score) from tb_class where course语文; select course, min(score) from tb_class where course英语;COUNT(*)统计整张表的行数COUNT(列名)统计该列非 NULL 的行数如果列允许 NULL两个结果可能不一致。SUM、AVG自动忽略 NULL 值但列里全是 NULL 时AVG返回 NULL 而不是 0。MAX、MIN对字符串按字典序比较不是按长度这个细节在统计课程名称时容易踩。聚合函数最常见的坑发生在 WHERE 和 HAVING 混用时。记住一条规则WHERE 在分组前过滤行HAVING 在分组后过滤组。比如「统计平均分大于 90 的班级」# 先按班级分组再用 HAVING 过滤平均分大于 90 的组 select class_id, avg(score) as avg_score from tb_score group by class_id having avg_score 90;如果把having avg_score 90写成where avg_score 90数据库会直接报错因为 WHERE 在分组之前执行那时聚合结果还不存在。分组统计的完整形态是多个分组维度组合# 按年级和性别两个维度分组每组统计人数 select gradeId, sex, count(*) from student where gradeId in (2,3,4) group by gradeId, sex;结果里每个「年级-性别」组合占一行。注意SELECT查询的列必须出现在GROUP BY中否则在 MySQL 5.7 的ONLY_FULL_GROUP_BY模式下会直接报错。4.4 经典题型交换、排名与人流量的解题套路资源后半段的复杂查询难度明显上一个台阶。三道经典题几乎覆盖了实训里最吃技巧的三类写法CASE WHEN 做值交换、子查询做排名、自连接做连续区间判断。第一类「交换工资」本质是把某一列的值按条件互相替换# 用 CASE WHEN 把性别互换m 变 ff 变 m UPDATE tb_Salary SET sex CASE sex WHEN m THEN f ELSE m END;CASE sex WHEN m THEN f ELSE m END是一个表达式对每一行判断当前 sex 的值是 m 就改成 f否则改成 m。这种写法只执行一次 UPDATE 就把两类值对调如果分两步更新第一步把 m 改成 f第二步再按 f 改 m数据就会全部变成 m这就是为什么不能分步做交换。第二类「分数排名」用相关子查询数比自己分数高的行数# 去重排名数一数有多少个不同的分数大于等于当前分数 select Score, (select count(distinct score) from score where score s.score) as Rank from score as s order by Score desc;内层子查询对每一行都执行一次统计score s.score的不同分数个数。比如最高分 100 分统计出来 count 是 1第二名 99 分统计出来是 2这样就实现了排名。如果不用 DISTINCT会出现并列名次占位的问题比如两个第二第三就会从 4 开始数。第三类「体育馆人流量」考的是自连接找连续区间-- 三表自连接判断 id 连续的三天都超过 100 人 select distinct a.* from gymnasium a, gymnasium b, gymnasium c where a.visitors_flow100 and b.visitors_flow100 and c.visitors_flow100 and ( (a.id b.id-1 and b.id c.id - 1) -- a在中间b、c是后两天 or (a.id b.id-1 and a.id c.id 1) -- b、c是前后两天 or (a.id b.id1 and b.id c.id 1) -- a在最后c、b是前两天 ) order by a.id;同一张表自己和自己连接三次通过 id 相差 1 的关系找出「连续三天流量都达标」的日期。distinct去掉重复行是因为同一天可能出现在多个三元组里。这道题难点不在 SQL 语法而在你能不能想到用自连接把「连续三天」翻译成「a、b、c三个 id 构成三个连续整数」。5. 避坑检查实训评分环境里最常翻车的五个位置5.1 语句结尾与分号多写一个分号被判错现象答案在 Navicat 里能跑通粘贴到实训平台却提示语法错误。原因实训评分脚本经常把整段代码作为一条语句执行BEGIN ... END块内部如果多了分号或者最后一行后面留了空行加额外分号会被解析成两条语句第二条不完整就直接报错。解决每条语句末尾只保留一个分号自定义函数和存储过程用delimiter包裹的代码段结尾的分号和delimiter //要严格按题目示例写。本地复现时发现show create table ... \G这种以\G结尾的语句按行评分时尤其挑剔建议去掉\G用普通分号结尾。5.2 大小写与中文字符表名、列名和注释的坑现象查询结果和标准答案内容一致评分仍然判定错误。原因三个层面。第一实训库在 Linux 下表名区分大小写TB_EMP和tb_emp是两个表第二中文字符集不对插入的中文查询条件变成乱码后匹配不上第三代码编辑器里的智能引号把 ASCII 引号替换成中文引号course语文的中文引号会让 SQL 解析直接失败。解决表名和列名严格按题目描述写统一小写建库语句显式加DEFAULT CHARSETutf8写中文字符串时只用英文单引号写完检查一下引号两边的字重是否统一。5.3 外键约束与删除顺序先删子表还是先删父表现象删除父表记录时提示cannot delete or update a parent row。原因t_class 是父表t_student 通过外键引用 t_class.id只要子表里还有学生引用某个班级 id直接删父表记录就被外键约束拦住。解决先删子表里引用该班级的行再删父表记录。实训里如果题目只要求删父表但环境里子表已有数据可以先执行SET FOREIGN_KEY_CHECKS0;临时禁用外键检查删完再设回 1。注意这个命令只在当前会话有效不影响其他提交。5.4 GROUP BY 与 HAVING分组列没查全导致报错现象执行select class_id, count(*) from tb_score group by class_id;时报错提示列不在 GROUP BY 子句中。原因MySQL 5.7 及以上默认开启ONLY_FULL_GROUP_BYSELECT 的列必须在 GROUP BY 里出现否则拒绝执行。实训平台恰好是 5.7这条规则卡掉了很多人。解决SELECT 里只保留分组列和聚合函数。如果确实需要查非分组列用子查询或ANY_VALUE()绕过但实训里不推荐这种写法评分脚本可能不认。正确做法是先group by分组再在子查询外层取完整记录。5.5 修改类题目重复执行第二次跑答案直接失败现象同一个关卡在本机练习时第二次提交结果和第一次不一致甚至报错。原因评分沙箱每次提交会重置数据库但本地练习时不会。带AUTO_INCREMENT的表第二次插入id 会继续累加UPDATE 把性别从 m 改成 f再跑一遍条件就匹配不上了。解决本地练习前先DROP TABLE再重新建表或者每次跑之前执行TRUNCATE TABLE 表名;把数据清零。实训平台的线上提交不用担心这个但本地复现时一定要重置环境否则你会以为答案写错了实际是上一轮练习的数据残留导致的。6. 用一份自检 SQL 验证实训答案三分钟定位对错拿到这份 PDF 答案之后最高效的用法不是逐条抄而是把它当作「通关对比表」。每一关提交前我习惯先跑三条自检 SQL确认自己的语句和答案在逻辑上等价而不是在字符串上完全一致。第一类自检是结构检查。建表或改表类关卡提交前确认表存在、字段正确# 查看当前库里有哪些表 SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA DATABASE(); # 查看表字段类型和键标记 DESC t_emp; # 查看建表语句细节重点看 ENGINE 和 CHARSET SHOW CREATE TABLE t_emp;information_schema.TABLES查库内所有表名DESC看字段顺序和 PRI/MUL 标记SHOW CREATE TABLE看字符集和引擎。这三条合在一起能覆盖 80% 的 DDL 类关卡。第二类自检是等价条件比对。把答案里的 WHERE 改写成另一种表达确认结果一致-- 两种写法结果应完全相同 SELECT * FROM tb_score WHERE score BETWEEN 3000 AND 5000; SELECT * FROM tb_score WHERE score 3000 AND score 5000;如果两个结果的行数和内容一样说明查询条件没问题。如果不一样优先怀疑 BETWEEN 边界、LIKE 通配符位置、NULL 判断是不是用了等号。第三类自检是连接查询的总数校验。多表连接最容易翻倍数据用保留表的行数做对照# 左外连接的行数应该等于学生表总行数 SELECT COUNT(*) FROM tb_student s LEFT JOIN tb_class c ON s.class_id c.id; # 学生表总行数 SELECT COUNT(*) FROM tb_student;左外连接保留学生表全部行第一个 COUNT 应该等于第二个 COUNT。如果第一个大于第二个说明 ON 条件导致一对多膨胀如果第一个小于第二个说明连接条件漏掉了某些匹配关系。这个技巧在真实业务里写报表时极其好用我每次完成带 JOIN 的查询都会用保留表的行数做一次总数校验能拦下大量因为一对多连接导致的数据翻倍。从那以后我每次跑实训答案和实际项目 SQL 前都强制走一遍这三板斧结构查一次、等价条件比对一次、连接总数校验一次。这套习惯帮我省下了大量查错时间也希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表