一、前言

在单表操作(DDL/DML/DQL)的基础上,多表操作是 MySQL 进阶的核心,也是实际开发中处理复杂业务数据的必备技能。本文将从 “多表关系约束” 到 “多表查询”,再到 “高级语法”,全方位拆解 MySQL 多表操作的核心知识点:涵盖一对多外键约束、交叉连接、内连接、外连接、子查询、CASE WHEN 等,结合学生成绩 / 班级管理实战案例(与前文知识点关联),让零基础的你也能轻松掌握!

二、多表关系基础:外键约束(FOREIGN KEY)

2.1 核心概念

  • 主表:拥有主键的表(一的一方,如班级表);
  • 从表(外表):拥有外键的表(多的一方,如学生表);
  • 外键约束:保证从表的外键列值必须是主表主键列已存在的值,避免脏数据,维护多表数据一致性。

2.2 一对多关系建表原则

在 “多” 的一方新增一列作为外键,关联 “一” 的一方的主键(如学生表的class_id关联班级表的id,与前文学生成绩表场景呼应)。

2.3 外键约束实战(班级 - 学生案例)

2.3.1 语法格式

表格

操作 语法
建表时添加外键 CREATE TABLE 从表(列定义..., CONSTRAINT 外键名 FOREIGN KEY(外键列) REFERENCES 主表(主键列));
建表后添加外键 ALTER TABLE 从表 ADD CONSTRAINT 外键名 FOREIGN KEY(外键列) REFERENCES 主表(主键列);
删除外键 ALTER TABLE 从表 DROP FOREIGN KEY 外键名;
2.3.2 实操案例
-- 切换数据库(与前文学生成绩表共用day02库)
USE day02;

-- 1. 创建主表:班级表(一的一方,对应前文student_score表的class字段)
CREATE TABLE class_info(
    id INT PRIMARY KEY AUTO_INCREMENT,  -- 班级ID(主键)
    class_name VARCHAR(20)              -- 班级名称(如一班、二班、三班)
);

-- 2. 创建从表:学生信息表(多的一方,添加外键约束)
CREATE TABLE student_info(
    id INT PRIMARY KEY AUTO_INCREMENT,  -- 学生ID(主键)
    stu_name VARCHAR(20),               -- 学生姓名
    stu_age INT,                        -- 学生年龄
    class_id INT,                       -- 外键列:关联班级表ID
    -- 建表时添加外键约束(推荐命名规范:fk_主表名_从表名)
    CONSTRAINT fk_class_student FOREIGN KEY(class_id) REFERENCES class_info(id)
);

-- 3. 插入主表数据(班级表,与前文student_score表的班级一致)
INSERT INTO class_info VALUES(null, '一班'), (null, '二班'), (null, '三班'), (null, '四班');

-- 4. 插入从表数据(学生表,沿用前文学生姓名)
INSERT INTO student_info VALUES(null, '张三', 18, 1);   -- 合法:class_id=1(一班)存在
INSERT INTO student_info VALUES(null, '李四', 19, 1); -- 合法:class_id=1(一班)存在
INSERT INTO student_info VALUES(null, '王五', 18, 2); -- 合法:class_id=2(二班)存在
INSERT INTO student_info VALUES(null, '赵六', 20, 2);    -- 合法:class_id=2(二班)存在
-- INSERT INTO student_info VALUES(null, '钱七', 19, 10);    -- 非法:class_id=10不存在于班级表

-- 5. 查看数据(关联前文学生成绩表场景)
SELECT * FROM class_info;
SELECT * FROM student_info;

-- 6. 删除外键约束
ALTER TABLE student_info DROP FOREIGN KEY fk_class_student;

-- 7. 建表后添加外键(前提:从表外键列数据均合法)
ALTER TABLE student_info ADD FOREIGN KEY(class_id) REFERENCES class_info(id);
2.3.3 注意事项
  1. 实际开发中,外键约束使用较少(会降低插入 / 删除效率),更多通过代码层面控制数据一致性;
  2. 外键列的数据类型必须与主表主键列一致;
  3. 删除主表数据前,需先删除从表关联数据(或设置外键级联操作);
  4. 本案例与前文student_score表的班级字段完全对应,可联动查询学生成绩 + 班级信息。

三、多表查询核心:连接查询

3.1 环境准备(学生 - 成绩案例,关联前文)

-- 1. 复用前文创建的student_score表(学生成绩表)
-- 若未创建,执行以下语句(与前文保持一致)
CREATE TABLE IF NOT EXISTS student_score(
    id         INT PRIMARY KEY AUTO_INCREMENT, 
    name       VARCHAR(20),    
    age        INT,            
    gender     CHAR(1),        
    score      DOUBLE,         
    class      VARCHAR(20)     
);
INSERT INTO student_score(id,name,age,gender,score,class) VALUES
(null,'张三',18,'男',88,'一班'),
(null,'李四',19,'男',92,'一班'),
(null,'王五',18,'男',65,'二班'),
(null,'赵六',20,'女',78,'二班'),
(null,'钱七',19,'女',95,'三班'),
(null,'孙八',18,'男',58,'三班'),
(null,'周九',19,'女',82,'一班'),
(null,'吴十',20,'男',45,'二班'),
(null,'郑十一',18,'女',90,'三班'),
(null,'王十二',19,'男',73,'一班');

-- 2. 班级表已在2.3.2中创建,补充关联字段(便于多表查询)
ALTER TABLE student_score ADD COLUMN class_id INT;
-- 更新student_score的class_id(关联class_info表)
UPDATE student_score SET class_id = 1 WHERE class = '一班';
UPDATE student_score SET class_id = 2 WHERE class = '二班';
UPDATE student_score SET class_id = 3 WHERE class = '三班';

-- 查看关联后的数据
SELECT * FROM student_score;
SELECT * FROM class_info;

3.2 交叉连接(CROSS JOIN)

3.2.1 核心说明
  • 结果:两张表的笛卡尔积(表 A 行数 × 表 B 行数),产生大量脏数据;
  • 用途:几乎不用于实际开发,仅用于理解连接查询底层逻辑。
3.2.2 语法 & 案例
-- 格式1:逗号分隔
SELECT * FROM student_score, class_info;

-- 格式2:JOIN关键字
SELECT * FROM student_score JOIN class_info;

3.3 内连接(INNER JOIN)★★★

3.3.1 核心说明
  • 结果:两张表的交集(仅返回满足关联条件的数据);
  • 场景:查询 “有关联关系” 的数据(如学生成绩 + 对应班级名称)。
3.3.2 语法 & 案例

表格

类型 语法 说明
隐式内连接 SELECT * FROM 表1, 表2 WHERE 关联条件; 简洁但可读性差
显式内连接 SELECT * FROM 表1 INNER JOIN 表2 ON 关联条件; 推荐(INNER 可省略),可读性 / 效率更高
-- 1. 隐式内连接(查询学生成绩+对应班级名称)
SELECT ss.name, ss.score, ci.class_name 
FROM student_score ss, class_info ci 
WHERE ss.class_id = ci.id;
-- 简化写法(无重名字段时)
SELECT name, score, class_name 
FROM student_score, class_info 
WHERE class_id = id;

-- 2. 显式内连接(推荐)
SELECT ss.name AS 学生姓名, ss.score AS 考试分数, ci.class_name AS 班级名称
FROM student_score ss INNER JOIN class_info ci 
ON ss.class_id = ci.id;
-- 省略INNER(效果一致)
SELECT ss.name AS 学生姓名, ss.score AS 考试分数, ci.class_name AS 班级名称
FROM student_score ss JOIN class_info ci 
ON ss.class_id = ci.id;

3.4 外连接(OUTER JOIN)★★★

3.4.1 核心说明

表格

类型 结果 场景
左外连接(LEFT JOIN) 左表全集 + 两表交集 需保留左表所有数据(如查询所有学生成绩,即使无对应班级)
右外连接(RIGHT JOIN) 右表全集 + 两表交集 需保留右表所有数据(如查询所有班级,即使无对应学生)
满外连接(FULL JOIN) 左表全集 + 右表全集 + 交集 MySQL 不直接支持,需用 UNION 合并左右外连接
3.4.2 语法 & 案例
-- 1. 左外连接(OUTER可省略)
-- 查询所有学生成绩,无对应班级则显示NULL(本案例无NULL,仅演示语法)
SELECT ss.name AS 学生姓名, ss.score AS 考试分数, ci.class_name AS 班级名称
FROM student_score ss LEFT OUTER JOIN class_info ci 
ON ss.class_id = ci.id;
-- 省略OUTER(推荐)
SELECT ss.name AS 学生姓名, ss.score AS 考试分数, ci.class_name AS 班级名称
FROM student_score ss LEFT JOIN class_info ci 
ON ss.class_id = ci.id;

-- 2. 右外连接(OUTER可省略)
-- 查询所有班级,无对应学生则显示NULL(如四班无学生)
SELECT ss.name AS 学生姓名, ss.score AS 考试分数, ci.class_name AS 班级名称
FROM student_score ss RIGHT OUTER JOIN class_info ci 
ON ss.class_id = ci.id;
-- 省略OUTER(推荐)
SELECT ss.name AS 学生姓名, ss.score AS 考试分数, ci.class_name AS 班级名称
FROM student_score ss RIGHT JOIN class_info ci 
ON ss.class_id = ci.id;

-- 3. 满外连接(MySQL兼容写法)
-- UNION:合并并去重;UNION ALL:合并不去重(效率更高)
SELECT ss.name AS 学生姓名, ss.score AS 考试分数, ci.class_name AS 班级名称
FROM student_score ss LEFT JOIN class_info ci ON ss.class_id = ci.id
UNION ALL
SELECT ss.name AS 学生姓名, ss.score AS 考试分数, ci.class_name AS 班级名称
FROM student_score ss RIGHT JOIN class_info ci ON ss.class_id = ci.id;

四、高级查询语法

4.1 子查询

4.1.1 核心概念
  • 定义:一个 SQL 的查询条件依赖另一个 SQL 的结果(内层为子查询,外层为主查询);
  • 场景:分步查询的逻辑合并(如 “查询每个班级的最高分学生”,关联前文聚合查询知识点)。
4.1.2 语法 & 案例
-- 需求:查询三班分数最高的学生信息(关联前文student_score表)
-- 分步实现
-- Step1:查询三班的最高分数
SELECT MAX(score) FROM student_score WHERE class = '三班';
-- Step2:根据最高分数+班级查学生
SELECT * FROM student_score WHERE class = '三班' AND score = (SELECT MAX(score) FROM student_score WHERE class = '三班');

-- 实际开发优化写法(连接查询替代子查询,效率更高)
SELECT ss.* 
FROM student_score ss
JOIN (SELECT class, MAX(score) AS max_score FROM student_score GROUP BY class) t1
ON ss.class = t1.class AND ss.score = t1.max_score
WHERE ss.class = '三班'; -- 筛选三班

4.2 CASE WHEN 条件语法 ★★★

4.2.1 核心说明
  • 作用:实现 “行转列” 或 “条件赋值”(类似编程语言的 if-else);
  • 场景:将分数转换为等级(与前文学生成绩表强关联)。
4.2.2 语法格式

表格

格式 语法 适用场景
通用格式 CASE WHEN 条件1 THEN 结果1 WHEN 条件2 THEN 结果2 ELSE 结果n END [AS 别名]; 任意条件判断
简化格式 CASE 字段名 WHEN 值1 THEN 结果1 WHEN 值2 THEN 结果2 ELSE 结果n END [AS 别名]; 同一字段的等于判断
4.2.3 实操案例(学生成绩等级划分)
-- 需求:将学生分数转换为等级(<60→不及格,60-80→及格,80-90→良好,≥90→优秀)
-- 通用格式(推荐,兼容性强)
SELECT
    name AS 学生姓名,
    score AS 考试分数,
    class AS 班级,
    CASE
        WHEN score < 60 THEN '不及格'
        WHEN score >= 60 AND score < 80 THEN '及格'
        WHEN score >= 80 AND score < 90 THEN '良好'
        WHEN score >= 90 THEN '优秀'
        ELSE '无成绩'
    END AS 成绩等级
FROM student_score;

-- 拓展:结合班级表,查询学生姓名+班级名称+成绩等级
SELECT
    ss.name AS 学生姓名,
    ci.class_name AS 班级名称,
    ss.score AS 考试分数,
    CASE
        WHEN ss.score < 60 THEN '不及格'
        WHEN ss.score >= 60 AND ss.score < 80 THEN '及格'
        WHEN ss.score >= 80 AND ss.score < 90 THEN '良好'
        WHEN ss.score >= 90 THEN '优秀'
        ELSE '无成绩'
    END AS 成绩等级
FROM student_score ss
JOIN class_info ci ON ss.class_id = ci.id;

4.3 拓展知识点(进阶方向)

4.3.1 自关联查询
  • 定义:表自身与自身连接查询(如班级表扩展:年级→班级→小组);
  • 语法:SELECT * FROM class_info t1 JOIN class_info t2 ON t1.id = t2.parent_id;
4.3.2 窗口函数
  • 作用:实现分组内排序、排名、累加等(如每个班级内成绩排名);
  • 示例:
SELECT
    name AS 学生姓名,
    class AS 班级,
    score AS 考试分数,
    RANK() OVER(PARTITION BY class ORDER BY score DESC) AS 班级内排名
FROM student_score;

五、实操避坑指南

  1. 外键约束陷阱:MySQL 中删除主表数据前,需先删除从表关联数据,否则报错;
  2. 连接查询别名:多表查询时务必给表加别名(如 ss 代表 student_score),避免字段冲突;
  3. NULL 值处理:外连接结果可能包含 NULL,需用 IFNULL () 处理(如IFNULL(ci.class_name, '未分配班级'));
  4. 子查询效率:复杂场景优先用连接查询替代子查询(减少数据库扫描次数);
  5. UNION vs UNION ALL:无需去重时用 UNION ALL(效率远高于 UNION);
  6. 关联字段一致性:多表查询时确保关联字段(如 class_id)数据类型 / 值一致,否则无匹配结果。

六、总结

核心要点回顾

  1. 多表关系中,一对多需在 “多” 的一方加外键,本案例将班级表与前文学生成绩表关联,贴合实际业务场景;
  2. 多表查询核心:内连接查交集(学生 + 对应班级),左 / 右外连接查 “全集 + 交集”,满外连接需用 UNION 合并;
  3. 子查询适合简单分步逻辑,复杂场景优先用连接查询(如每个班级最高分学生);
  4. CASE WHEN 可实现成绩等级划分等行级条件判断,是业务报表的常用语法;
  5. 所有案例均沿用前文学生成绩表的基础数据,知识点前后联动,便于系统掌握。

进阶学习建议

  1. 练习经典场景:如 “查询每个班级的及格率”“查询成绩优秀的学生所属班级”;
  2. 学习性能优化:索引对多表查询的影响、EXPLAIN 分析执行计划;
  3. 拓展多表关系:多对多(如学生 - 课程表,需中间表)、一对一(如学生 - 学籍表)。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐