当前位置: 首页 > news >正文

MySQL 多表查询完全指南:JOIN 与子查询

引言

在前面的 MySQL 文章中,我们学习了单表的 CRUD 操作、事务、索引和视图。然而,实际开发中数据往往分布在多个表中——学生表、课程表、成绩表……需要联合查询才能得到完整信息。

多表查询是 SQL 的核心能力,主要包括连接查询(JOIN)子查询两大类。本文将通过大量示例,彻底讲透各种 JOIN 的用法和子查询的写法。

第一部分:准备测试数据

-- 创建学生表 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20) NOT NULL, age INT ); -- 创建课程表 CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, teacher VARCHAR(20) ); -- 创建成绩表(关联学生和课程) CREATE TABLE score ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT, course_id INT, score INT, FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (course_id) REFERENCES course(id) ); -- 插入学生数据 INSERT INTO student VALUES (1, '张三', 20), (2, '李四', 22), (3, '王五', 21), (4, '赵六', 23); -- 插入课程数据 INSERT INTO course VALUES (1, 'MySQL数据库', '王老师'), (2, 'C++编程', '李老师'), (3, '数据结构', '张老师'); -- 插入成绩数据 INSERT INTO score VALUES (1, 1, 1, 90), -- 张三 - MySQL - 90 (2, 1, 2, 85), -- 张三 - C++ - 85 (3, 2, 1, 78), -- 李四 - MySQL - 78 (4, 2, 3, 92), -- 李四 - 数据结构 - 92 (5, 3, 2, 88); -- 王五 - C++ - 88 -- 注意:赵六没有选任何课

当前数据状态

第二部分:JOIN 连接查询

一、JOIN 类型总览

JOIN 类型含义结果
INNER JOIN内连接只返回两表都匹配的行
LEFT JOIN左外连接左表全部 + 右表匹配的行(右表无匹配填 NULL)
RIGHT JOIN右外连接右表全部 + 左表匹配的行(左表无匹配填 NULL)
CROSS JOIN交叉连接笛卡尔积,两表所有行组合
自连接表和自己 JOIN用于树形结构、上下级关系

二、INNER JOIN(内连接)—— 最常用

-- 查询每个学生选了什么课、得了多少分 -- 写法1:INNER JOIN(推荐) SELECT s.name AS 学生, c.name AS 课程, sc.score AS 成绩 FROM student s INNER JOIN score sc ON s.id = sc.student_id INNER JOIN course c ON sc.course_id = c.id; -- 写法2:WHERE 隐式连接(老式写法,不推荐) SELECT s.name, c.name, sc.score FROM student s, score sc, course c WHERE s.id = sc.student_id AND sc.course_id = c.id;

结果

三、LEFT JOIN(左外连接)

-- 查询所有学生及其选课情况(包括没选课的学生) SELECT s.name AS 学生, c.name AS 课程, sc.score AS 成绩 FROM student s LEFT JOIN score sc ON s.id = sc.student_id LEFT JOIN course c ON sc.course_id = c.id;

结果

典型应用:找出没有选课的学生。

SELECT s.name AS 学生 FROM student s LEFT JOIN score sc ON s.id = sc.student_id WHERE sc.student_id IS NULL; -- 结果:赵六

四、CROSS JOIN(交叉连接)

-- 两表所有行一一组合(笛卡尔积) SELECT s.name, c.name FROM student s CROSS JOIN course c; -- 4 个学生 × 3 门课 = 12 行

五、自连接

-- 假设有员工表,查询每个员工的上级 CREATE TABLE emp ( id INT, name VARCHAR(20), manager_id INT ); INSERT INTO emp VALUES (1, '老板', NULL), (2, '经理A', 1), (3, '员工B', 2); -- 自连接:查员工和上级 SELECT e.name AS 员工, m.name AS 上级 FROM emp e LEFT JOIN emp m ON e.manager_id = m.id;

第三部分:子查询

一、什么是子查询

子查询是一个嵌套在另一个 SQL 语句中的 SELECT 语句。它可以出现在 WHERE、FROM、SELECT 子句中。

二、WHERE 子查询(最常用)

-- 查询成绩高于平均分的学生 SELECT s.name, sc.score FROM student s JOIN score sc ON s.id = sc.student_id WHERE sc.score > (SELECT AVG(score) FROM score); -- 查询选了 MySQL 数据库的学生姓名 SELECT name FROM student WHERE id IN ( SELECT student_id FROM score WHERE course_id = (SELECT id FROM course WHERE name = 'MySQL数据库') );

三、FROM 子查询

-- 将子查询结果作为临时表 SELECT avg_score.course_name, avg_score.avg FROM ( SELECT c.name AS course_name, AVG(sc.score) AS avg FROM course c JOIN score sc ON c.id = sc.course_id GROUP BY c.name ) AS avg_score WHERE avg_score.avg > 80;

四、SELECT 子查询

-- 每个学生及其最高分 SELECT s.name, ( SELECT MAX(sc.score) FROM score sc WHERE sc.student_id = s.id ) AS 最高分 FROM student s;

五、EXISTS 子查询

-- 查询有选课记录的学生 SELECT name FROM student s WHERE EXISTS ( SELECT 1 FROM score sc WHERE sc.student_id = s.id ); -- 查询没有选课的学生 SELECT name FROM student s WHERE NOT EXISTS ( SELECT 1 FROM score sc WHERE sc.student_id = s.id );
子查询写法使用场景
WHERE x IN (子查询)判断某值是否在子查询结果中
WHERE x > (子查询)和子查询结果比较(子查询必须返回单值)
WHERE EXISTS (子查询)判断子查询是否有结果
FROM (子查询) AS t子查询结果作为临时表
SELECT (子查询)子查询结果作为一列

第四部分:UNION 联合查询

UNION 把多个 SELECT 的结果合并成一个结果集。

-- 查询选了 MySQL 或 C++ 的学生(去重) SELECT DISTINCT s.name FROM student s JOIN score sc ON s.id = sc.student_id JOIN course c ON sc.course_id = c.id WHERE c.name = 'MySQL数据库' UNION SELECT DISTINCT s.name FROM student s JOIN score sc ON s.id = sc.student_id JOIN course c ON sc.course_id = c.id WHERE c.name = 'C++编程';
关键字区别
UNION合并并去重
UNION ALL合并但不去重(更快)

第五部分:聚合函数与 GROUP BY

一、常用聚合函数

函数作用
COUNT(*)统计行数
SUM(列)求和
AVG(列)平均值
MAX(列)最大值
MIN(列)最小值

二、GROUP BY 分组统计

-- 每个学生的平均分 SELECT s.name, AVG(sc.score) AS 平均分 FROM student s JOIN score sc ON s.id = sc.student_id GROUP BY s.name; -- 每门课的平均分、最高分、最低分 SELECT c.name AS 课程, AVG(sc.score) AS 平均分, MAX(sc.score) AS 最高分, MIN(sc.score) AS 最低分 FROM course c JOIN score sc ON c.id = sc.course_id GROUP BY c.name;

三、HAVING 过滤分组

WHERE在分组前过滤行,HAVING在分组后过滤组。

-- 平均分大于 85 的课程 SELECT c.name, AVG(sc.score) AS avg_score FROM course c JOIN score sc ON c.id = sc.course_id GROUP BY c.name HAVING avg_score > 85;
关键字作用执行时机
WHERE过滤行GROUP BY 之前
HAVING过滤组GROUP BY 之后

总结

一、核心要点

知识点关键语法
内连接INNER JOIN ... ON
左连接LEFT JOIN ... ON
子查询WHERE x IN (SELECT ...)
联合查询UNION / UNION ALL
分组统计GROUP BY ... HAVING

二、SQL 执行顺序

FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT

三、一句话记忆

多表查询用 JOIN...ON 关联,左连接保留左表全量,子查询嵌套在 WHERE/FROM/SELECT 中,GROUP BY 配合聚合函数做分组统计,HAVING 过滤分组结果。

http://www.jsqmd.com/news/892943/

相关文章:

  • 21.8k stars!告别“读代码读到怀疑人生“:这个开源工具让任何代码库秒变可视化知识图谱!
  • 5分钟掌握Zotero Style:让文献管理变得优雅高效的终极指南
  • 【考试总结】2026年5月23日系统架构设计师考试总结
  • DeepSeek降AI提示词大全+热门降AI工具横向测评:我把AI率干到了6%! - 殷念写论文
  • 2026 年论文双检通关指南:9 款查重 + 降 AIGC 工具横评
  • 点云扫描 vs 高斯重建:数字孪生别再乱选!一个落地、一个只能看
  • 游标码光电角度编码器原理教育八讲(一)
  • 昇腾NPU上的NumPy兼容层:asnumpy如何让Python代码自动加速3倍
  • 【2026年郑州再生资源回收口碑推荐】 - 资讯速览
  • J Hepatol(IF=33.0)英国帝国理工学院:基于机器学习的影像组学模型在预测肝细胞癌免疫治疗结局中优于临床生物标志物
  • 别再只会点灯了!用STM32CubeMx配置GPIO输出模式(推挽/开漏)的实战避坑指南
  • 面试官:Plan-Execute-Replan 和 ReAct 有啥区别?
  • 3步完成BetterNCM插件管理器安装:彻底改造网易云音乐体验的智能解决方案
  • O.o?MCP 的尽头是情趣玩具?先别急,搞懂它到底是什么
  • AI视频生成:为什么它正在改变创作方式?
  • 【Lovable平台开发生死线】:3类致命本地化缺陷、5个合规雷区、1套GDPR+ISO 17100双认证落地模板
  • 鸿蒙 地理编码:正地理编码与逆地理编码
  • java中 (whlie)、 (if else)、( for)、(switch)
  • ESP32内存不够用?手把手教你用Platformio开启4MB PSRAM(附串口验证代码)
  • 2026年国产外夹式超声波流量计十大品牌深度测评:技术实力、行业应用与选型指南 - 仪表品牌排行榜
  • 【算法分析与设计】第10篇:下界理论与NP完全性初步
  • 京东三面:Function Calling 和 MCP 都能做工具调用,那具体什么场景下该选哪个?
  • Node.js:现代 Web 开发的高性能 JavaScript 运行时
  • 高誉 4+5 网红机油赋能青岛汽修门店,青岛莱茵特斯诚邀合作 - 资讯速览
  • 避开 Agent 落地大坑,业内大咖复盘行业真相
  • 易语言选择框批量操作:从单选互斥到一键全选/取消的实战解析
  • Keil MDK工程里printf中文正常,一换编辑器就乱码?手把手教你排查编码‘隐形杀手’
  • 去中心化Agent网络性能瓶颈大起底:TPS突破8,400的共识层改造方案(附可复现压测数据集)
  • P16307 [蓝桥杯 2026 省 Java/Python 研究生组] 抓取卡牌 题解
  • 【算法分析与设计】第11篇:图的表示与遍历算法:BFS与DFS的扩展性质