MySQL多表连接完全指南:内连接与外连接超详细讲解

🔗 掌握多表连接,让你的数据查询能力提升一个档次!本文用最直观的方式讲解MySQL连接查询的核心概念。

一、前言:为什么需要多表连接?

在实际项目中,数据通常分散在多个表中。比如:

学生表:存学生基本信息

成绩表:存学生考试成绩

班级表:存班级信息

如果我们想查询"学生姓名 + 班级名称 + 各科成绩",就需要同时从多个表获取数据。这时候,多表连接就派上用场了!

二、准备工作:创建示例数据库

2.1 创建学生管理系统数据库

bash 复制代码
sql
-- 创建数据库
CREATE DATABASE IF NOT EXISTS school_db;
USE school_db;

-- 1. 创建学生表
CREATE TABLE students (
    student_id INT PRIMARY KEY AUTO_INCREMENT,
    student_name VARCHAR(50) NOT NULL,
    gender ENUM('男', '女') DEFAULT '男',
    age INT,
    class_id INT  -- 关联班级表的ID
);

-- 2. 创建班级表
CREATE TABLE classes (
    class_id INT PRIMARY KEY AUTO_INCREMENT,
    class_name VARCHAR(50) NOT NULL,
    teacher VARCHAR(50)
);

-- 3. 创建成绩表
CREATE TABLE scores (
    score_id INT PRIMARY KEY AUTO_INCREMENT,
    student_id INT,
    subject VARCHAR(50),
    score DECIMAL(5,2),
    exam_date DATE
);

-- 4. 创建选课表(用于演示多对多关系)
CREATE TABLE course_selection (
    selection_id INT PRIMARY KEY AUTO_INCREMENT,
    student_id INT,
    course_id INT
);

-- 5. 创建课程表
CREATE TABLE courses (
    course_id INT PRIMARY KEY AUTO_INCREMENT,
    course_name VARCHAR(50),
    credit INT
);

2.2 插入测试数据

bash 复制代码
sql
-- 插入班级数据
INSERT INTO classes (class_name, teacher) VALUES
('一年级一班', '张老师'),
('一年级二班', '李老师'),
('二年级一班', '王老师'),
('二年级二班', '赵老师');

-- 插入学生数据
INSERT INTO students (student_name, gender, age, class_id) VALUES
('张三', '男', 18, 1),
('李四', '女', 17, 1),
('王五', '男', 19, 2),
('赵六', '女', 18, 2),
('钱七', '男', 20, 3),
('孙八', '女', 19, 3),
('周九', '男', 18, NULL),  -- 注意:这个学生没有分配班级
('吴十', '女', 17, NULL);  -- 注意:这个学生没有分配班级

-- 插入成绩数据
INSERT INTO scores (student_id, subject, score, exam_date) VALUES
(1, '数学', 85.5, '2023-06-15'),
(1, '语文', 90.0, '2023-06-16'),
(2, '数学', 78.0, '2023-06-15'),
(2, '语文', 88.5, '2023-06-16'),
(3, '数学', 92.0, '2023-06-15'),
(4, '语文', 76.5, '2023-06-16'),
(5, '数学', 89.0, '2023-06-15'),
(6, '语文', 94.5, '2023-06-16'),
(9, '数学', 100.0, '2023-06-15');  -- 注意:学生ID 9不存在于学生表

-- 插入课程数据
INSERT INTO courses (course_name, credit) VALUES
('高等数学', 4),
('大学英语', 3),
('计算机基础', 2),
('数据结构', 4);

-- 插入选课数据
INSERT INTO course_selection (student_id, course_id) VALUES
(1, 1), (1, 2),
(2, 1), (2, 3),
(3, 2), (3, 4),
(4, 1), (4, 2), (4, 3);

2.3 查看各表数据概览

bash 复制代码
sql
-- 学生表
SELECT * FROM students;
-- 班级表  
SELECT * FROM classes;
-- 成绩表
SELECT * FROM scores;
-- 课程表
SELECT * FROM courses;
-- 选课表
SELECT * FROM course_selection;

三、内连接查询(INNER JOIN)

3.1 什么是内连接?

内连接就像"相亲":只返回两个表都匹配的记录。如果某条记录在一个表存在,但在另一个表找不到对应记录,就不会出现在结果中。

3.2 基本语法

bash 复制代码
sql
-- 写法1:使用INNER JOIN关键字(推荐)
SELECT 列名
FROM 表1
INNER JOIN 表2 ON 连接条件;

-- 写法2:使用WHERE子句(传统写法)
SELECT 列名
FROM 表1, 表2
WHERE 连接条件;

3.3 实例演示:查询学生及其班级信息

bash 复制代码
sql
-- 查询所有学生及其所在班级
SELECT 
    s.student_id,
    s.student_name,
    s.gender,
    s.age,
    c.class_name,
    c.teacher
FROM students s  -- s是students表的别名
INNER JOIN classes c ON s.class_id = c.class_id;
查询结果:

text
+------------+--------------+--------+-----+--------------+-----------+
| student_id | student_name | gender | age | class_name   | teacher   |
+------------+--------------+--------+-----+--------------+-----------+
|          1 | 张三         | 男     |  18 | 一年级一班   | 张老师    |
|          2 | 李四         | 女     |  17 | 一年级一班   | 张老师    |
|          3 | 王五         | 男     |  19 | 一年级二班   | 李老师    |
|          4 | 赵六         | 女     |  18 | 一年级二班   | 李老师    |
|          5 | 钱七         | 男     |  20 | 二年级一班   | 王老师    |
|          6 | 孙八         | 女     |  19 | 二年级一班   | 王老师    |
+------------+--------------+--------+-----+--------------+-----------+

🔍 重要发现:

结果只有6条记录

学生"周九"和"吴十"没有出现在结果中(因为他们没有班级)

这就是内连接的特点:只返回两个表都有的匹配记录

3.4 多表内连接:查询学生成绩详情

bash 复制代码
sql
-- 查询学生姓名、班级、科目和成绩
SELECT 
    s.student_name,
    c.class_name,
    sc.subject,
    sc.score,
    sc.exam_date
FROM students s
INNER JOIN classes c ON s.class_id = c.class_id
INNER JOIN scores sc ON s.student_id = sc.student_id;

3.5 内连接加条件筛选

bash 复制代码
sql
-- 查询一年级学生的数学成绩
SELECT 
    s.student_name,
    c.class_name,
    sc.score
FROM students s
INNER JOIN classes c ON s.class_id = c.class_id
INNER JOIN scores sc ON s.student_id = sc.student_id
WHERE c.class_name LIKE '一年级%' 
  AND sc.subject = '数学'
ORDER BY sc.score DESC;

四、外连接查询(OUTER JOIN)

4.1 外连接的概念

外连接就像"相亲+展示":除了返回匹配的记录,还会返回一个表中存在但另一个表没有匹配的记录。

4.2 三种外连接类型

📊 对比表格

连接类型 含义 关键字

左外连接 返回左表所有记录 + 右表匹配记录 LEFT JOIN

右外连接 返回右表所有记录 + 左表匹配记录 RIGHT JOIN

全外连接 返回两个表的所有记录 FULL JOIN

⚠️ 注意:MySQL不支持FULL JOIN,但可以通过UNION实现同样效果

4.3 左外连接(LEFT JOIN)

bash 复制代码
查询所有学生,包括没有班级的学生
sql
SELECT 
    s.student_id,
    s.student_name,
    c.class_name,
    c.teacher
FROM students s  -- 左表
LEFT JOIN classes c ON s.class_id = c.class_id  -- 左连接
ORDER BY s.student_id;
查询结果:

text
+------------+--------------+--------------+-----------+
| student_id | student_name | class_name   | teacher   |
+------------+--------------+--------------+-----------+
|          1 | 张三         | 一年级一班   | 张老师    |
|          2 | 李四         | 一年级一班   | 张老师    |
|          3 | 王五         | 一年级二班   | 李老师    |
|          4 | 赵六         | 一年级二班   | 李老师    |
|          5 | 钱七         | 二年级一班   | 王老师    |
|          6 | 孙八         | 二年级一班   | 王老师    |
|          7 | 周九         | NULL         | NULL      | ← 没有班级
|          8 | 吴十         | NULL         | NULL      | ← 没有班级
+------------+--------------+--------------+-----------+

🔍 重要发现:

所有学生都出现了(包括没有班级的周九和吴十)

对于没有班级的学生,班级信息显示为NULL

左连接保证左表(students)所有记录都会出现

bash 复制代码
找出没有班级的学生
sql
SELECT 
    s.student_id,
    s.student_name,
    s.age
FROM students s
LEFT JOIN classes c ON s.class_id = c.class_id
WHERE c.class_id IS NULL;

4.4 右外连接(RIGHT JOIN)

bash 复制代码
查询所有班级,包括没有学生的班级
sql
SELECT 
    c.class_id,
    c.class_name,
    c.teacher,
    s.student_name
FROM students s  -- 左表
RIGHT JOIN classes c ON s.class_id = c.class_id  -- 右连接
ORDER BY c.class_id;
查询结果:

text
+----------+--------------+-----------+--------------+
| class_id | class_name   | teacher   | student_name |
+----------+--------------+-----------+--------------+
|        1 | 一年级一班   | 张老师    | 张三         |
|        1 | 一年级一班   | 张老师    | 李四         |
|        2 | 一年级二班   | 李老师    | 王五         |
|        2 | 一年级二班   | 李老师    | 赵六         |
|        3 | 二年级一班   | 王老师    | 钱七         |
|        3 | 二年级一班   | 王老师    | 孙八         |
|        4 | 二年级二班   | 赵老师    | NULL         | ← 没有学生
+----------+--------------+-----------+--------------+

🔍 重要发现:

所有班级都出现了(包括没有学生的二年级二班)

对于没有学生的班级,学生信息显示为NULL

右连接保证右表(classes)所有记录都会出现

找出没有学生的班级

bash 复制代码
sql
SELECT 
    c.class_id,
    c.class_name,
    c.teacher
FROM students s
RIGHT JOIN classes c ON s.class_id = c.class_id
WHERE s.student_id IS NULL;

4.5 全外连接(FULL JOIN)的模拟实现

bash 复制代码
sql
-- MySQL不直接支持FULL JOIN,但可以用UNION模拟
-- 查询所有学生和所有班级的对应关系
SELECT 
    s.student_id,
    s.student_name,
    c.class_name
FROM students s
LEFT JOIN classes c ON s.class_id = c.class_id

UNION

SELECT 
    s.student_id,
    s.student_name,
    c.class_name
FROM students s
RIGHT JOIN classes c ON s.class_id = c.class_id
WHERE s.student_id IS NULL;

五、内连接 vs 外连接:对比分析

5.1 维恩图理解

bash 复制代码
text
内连接(INNER JOIN):
students表     classes表
   [交集部分] ← 只返回这个
   
左连接(LEFT JOIN):
[students全部] + 匹配的classes部分

右连接(RIGHT JOIN):
[classes全部] + 匹配的students部分

5.2 实际场景选择指南

实际场景示例

bash 复制代码
sql
-- 场景1:统计每个班级的学生人数(用内连接)
SELECT 
    c.class_name,
    COUNT(s.student_id) AS student_count
FROM classes c
INNER JOIN students s ON c.class_id = s.class_id
GROUP BY c.class_id;

-- 场景2:统计所有班级学生人数(包括没有学生的班级)
SELECT 
    c.class_name,
    COUNT(s.student_id) AS student_count
FROM classes c
LEFT JOIN students s ON c.class_id = s.class_id
GROUP BY c.class_id;

-- 场景3:查看所有学生的考试情况(包括没考试的学生)
SELECT 
    s.student_name,
    sc.subject,
    sc.score
FROM students s
LEFT JOIN scores sc ON s.student_id = sc.student_id;

-- 场景4:查看所有考试成绩(包括不存在的学生成绩)
SELECT 
    sc.score_id,
    sc.subject,
    sc.score,
    s.student_name
FROM scores sc
LEFT JOIN students s ON sc.student_id = s.student_id
WHERE s.student_id IS NULL;  -- 找出无效的成绩记录

六、复杂多表连接实战

6.1 多对多关系查询:学生选课情况

bash 复制代码
sql
-- 查询学生选课详情(学生 ↔ 课程 多对多)
SELECT 
    s.student_name,
    c.course_name,
    c.credit
FROM students s
INNER JOIN course_selection cs ON s.student_id = cs.student_id
INNER JOIN courses c ON cs.course_id = c.course_id
ORDER BY s.student_name, c.course_name;

6.2 自连接:查询同一班级的学生对

bash 复制代码
sql
-- 创建员工表演示自连接
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    emp_name VARCHAR(50),
    manager_id INT
);

INSERT INTO employees VALUES
(1, '张总', NULL),
(2, '李经理', 1),
(3, '王主管', 2),
(4, '赵员工', 3),
(5, '钱员工', 3);

-- 查询每个员工及其经理
SELECT 
    e.emp_name AS '员工姓名',
    m.emp_name AS '经理姓名'
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.emp_id;

6.3 带聚合函数的多表连接

bash 复制代码
sql
-- 查询每个班级的平均成绩
SELECT 
    c.class_name,
    AVG(sc.score) AS avg_score,
    COUNT(DISTINCT s.student_id) AS student_count,
    COUNT(sc.score_id) AS exam_count
FROM classes c
LEFT JOIN students s ON c.class_id = s.class_id
LEFT JOIN scores sc ON s.student_id = sc.student_id
GROUP BY c.class_id
HAVING exam_count > 0;  -- 只显示有考试成绩的班级

💡 总结与建议

掌握要点:

内连接用于精确匹配查询

左/右连接用于保证某表数据完整

多表连接时要理清表之间的关系

为连接字段创建索引提升性能

学习建议:

先理解业务需求,再选择连接类型

从简单连接开始,逐步增加复杂度

多用EXPLAIN分析查询性能

在实际项目中多练习

相关推荐
专注VB编程开发20年2 小时前
python图片验证码识别selenium爬虫--超级鹰实现自动登录,滑块,点击
数据库·python·mysql
智商偏低2 小时前
Postgresql导入几何数据(shp,geojson)的几种方式
数据库·postgresql
我是Superman丶2 小时前
在 PostgreSQL 中使用 JSONB 类型并结合 MyBatis-Plus 实现自动注入,主要有以下几种方案
数据库·postgresql·mybatis
五度易链-区域产业数字化管理平台2 小时前
「五度易链」行业标准信息数据库简介
大数据·数据库
qinyia3 小时前
通过本地构建解决Cartographer编译中absl依赖缺失问题
linux·运维·服务器·mysql·ubuntu
霖霖总总3 小时前
[小技巧65]深入 InnoDB 页的逻辑存储结构:16KB 页的逻辑全景解析
数据库·mysql
数研小生3 小时前
关键词搜索京东列表API技术对接指南
大数据·数据库·爬虫
野犬寒鸦3 小时前
从零起步学习并发编程 || 第五章:悲观锁与乐观锁的思想与实现及实战应用与问题
java·服务器·数据库·学习·语言模型
VX:Fegn08953 小时前
计算机毕业设计|基于springboot + vue云租车平台系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计