MySQL 多表查询从入门到精通:外键约束 + 连接查询 + 子查询(全网最易懂完整版)
在 MySQL 乃至所有关系型数据库的学习与实际开发中,单表操作只是基础,多表查询才是真正的核心与灵魂。无论是电商系统、后台管理、数据分析,几乎 90% 以上的业务需求,都离不开多张表的关联操作。
很多初学者学到多表时都会陷入混乱:表关系怎么分析?外键有什么用?内连接、左连接、右连接到底什么时候用?子查询又该怎么写?
这篇文章就带你从零到一,彻底吃透 MySQL 多表查询,全程附带可直接复制运行的实战案例,图文并茂,学完就能直接上手项目。
一、关系型数据库核心:表与表之间的关系
在设计数据库时,我们绝对不能把所有数据都塞进一张表里,那样会造成大量冗余、混乱、难以维护。正确的做法是:按业务拆分表,再通过关系将表关联起来。
常见的表关系有三种:
- 一对一例如:人 ↔ 身份证、用户 ↔ 用户详情特点:一张表的一条数据,只对应另一张表的一条数据。
- 一对多(最常用、最重要)例如:分类 ↔ 商品、部门 ↔ 员工、武功 ↔ 英雄特点:一方的数据,可以被多方引用。
- 多对多例如:学生 ↔ 课程、用户 ↔ 角色特点:需要借助中间表来维护关系。
一对多建表黄金原则
- “一” 的一方:主表,必须设置主键。
- “多” 的一方:从表,新增一列作为外键列,用来关联主表的主键。
一句话总结:在多的一方加一列,指向一的一方的主键,关系就建立了。
二、外键约束(Foreign Key):保证数据安全与一致性
外键是用来强制两张表之间的引用完整性的约束。

外键的作用
- 防止从表插入不存在的主表 ID
- 防止主表数据被从表引用时强行删除
- 保证数据一致性,避免脏数据
外键语法格式
CONSTRAINT 外键名称 FOREIGN KEY (从表外键字段) REFERENCES 主表(主键字段)
外键就像一个 “严格的校验器”,让多表数据不敢乱填。
三、实战案例一:英雄 & 武功表 —— 彻底搞懂连接查询
为了让大家直观理解,我们用武侠场景来学习:
- 一个英雄会一种武功
- 一种武功可以被多个英雄使用这是标准的一对多关系。
1. 创建数据库与表
-- 切换数据库
use day02;
-- 武功表(主表,一的一方)
create table kongfu
(
kid int primary key, -- 武功ID,主键
kname varchar(255) -- 武功名称
);
-- 英雄表(从表,多的一方)
create table hero
(
hid int primary key, -- 英雄ID
hname varchar(255), -- 英雄名称
kongfu_id int -- 外键列,关联武功表kid
);
2. 插入测试数据
-- 插入武功
insert into kongfu values
(1, '降龙十八掌'),
(2, '乾坤大挪移'),
(3, '猴子偷桃'),
(4, '天山折梅手');
-- 插入英雄
insert into hero values
(1, '鸠摩智', 9), -- 没有对应武功
(3, '乔峰', 1), -- 降龙十八掌
(4, '虚竹', 4), -- 天山折梅手
(5, '段誉', 12); -- 没有对应武功
3. 多表查询核心:三种连接一网打尽
(1)交叉连接(笛卡尔积)
千万不要在生产环境随便用!它的结果是:A 表总条数 × B 表总条数,数据会爆炸式增长。
-- 交叉连接
select * from hero,kongfu;
作用:仅用于理解连接原理,实际开发几乎不用。
(2)内连接(INNER JOIN):取两张表的交集
内连接只查询两边表能完全匹配上的数据。
- 显示内连接(推荐写法)
select * from hero h
inner join kongfu kf
on h.kongfu_id = kf.kid;
- 隐式内连接(老式写法)
select * from hero h ,kongfu kf
where h.kongfu_id = kf.kid;
执行结果:只显示乔峰、虚竹,因为他们的武功 ID 在武功表中存在。鸠摩智、段誉匹配不到,不会出现在结果中。
适用场景:只需要查询双方都存在关联的数据。
(3)外连接:保留某一张表的全部数据
外连接的核心是:以某一张表为基准,全部显示。
左外连接(LEFT JOIN)
以左边的表为基准,左表数据全部显示,右表能匹配就显示,匹配不上显示 NULL。
select * from hero h
left join kongfu kf
on h.kongfu_id = kf.kid;
结果:四个英雄全部显示,鸠摩智、段誉没有武功,显示 NULL。
右外连接(RIGHT JOIN)
以右边的表为基准,右表数据全部显示,左表匹配不上显示 NULL。
select * from hero h
right join kongfu kf
on h.kongfu_id = kf.kid;
结果:所有武功都显示,没有英雄使用的武功,对应的英雄字段为 NULL。
四、实战案例二:商品 & 分类 —— 外键 + 子查询实战
接下来我们用电商场景,学习更贴近企业开发的:外键约束 + 子查询。
1. 创建表并添加外键
-- 分类表(一)
create table category (
cid varchar(32) primary key ,
cname varchar(50)
);
-- 商品表(多)
create table products(
pid varchar(32) primary key ,
pname varchar(50),
price int,
flag varchar(2), -- 1上架 0下架
category_id varchar(32),
-- 添加外键
constraint products_fk
foreign key (category_id) references category (cid)
);
2. 插入测试数据
-- 分类数据
INSERT INTO category(cid,cname) VALUES
('c001','家电'),
('c002','服饰'),
('c003','化妆品'),
('c004','奢侈品');
-- 商品数据
INSERT INTO products(pid, pname,price,flag,category_id) VALUES
('p001','联想',5000,'1','c001'),
('p002','海尔',3000,'1','c001'),
('p003','雷神',5000,'1','c001'),
('p004','JACK JONES',800,'1','c002'),
('p005','真维斯',200,'1','c002'),
('p006','花花公子',440,'1','c002'),
('p007','劲霸',2000,'1','c002'),
('p008','香奈儿',800,'1','c003'),
('p009','相宜本草',200,'1','c003');
五、子查询:嵌套查询,SQL 进阶必备
子查询的本质:把一个 SQL 的查询结果,当作另一个 SQL 的条件或表来使用。
需求 1:查询哪些分类下有上架的商品
分析:
- 先从商品表中,找出所有上架商品的分类 ID
- 再去分类表中查询这些分类
-- 子查询实现
select * from category
where cid in (
select distinct category_id
from products
where flag = '1'
);
需求 2:统计每个分类下的商品数量(包含 0 个商品的分类)
这里需要用到 左连接 + 分组统计:
select
cname,
count(category_id) as total_cnt
from category c
left join products p
on c.cid = p.category_id
group by cname;
结果会显示:
- 家电:3 个
- 服饰:4 个
- 化妆品:2 个
- 奢侈品:0 个
非常贴合真实业务需求。
六、一张图彻底理解多表连接(必看总结)
为了方便记忆,你可以直接记住下面这个核心规律:
- 内连接(JOIN)只显示两边都能匹配的数据。
- 左连接(LEFT JOIN)左表全要,右表匹配不上填 NULL。
- 右连接(RIGHT JOIN)右表全要,左表匹配不上填 NULL。
- 子查询一个查询结果当作条件给另一个查询使用。
更多推荐




所有评论(0)