本文系统梳理 MySQL 中三种常见的表关联关系(一对一、一对多、多对多)的建表方式与外键约束写法,并配合 INNER JOIN / LEFT JOIN / RIGHT JOIN 三种联合查询的实战示例,帮助你快速掌握多表设计核心。
一、表关联关系速览
在数据库设计中,表与表之间的关联关系主要分为以下几种:
| 关联类型 |
核心设计逻辑 |
常见场景 |
| 一对一 (1:1) |
在从表外键列上添加 UNIQUE 约束,确保一条主表记录只对应一条从表记录 |
用户与身份证、用户与银行卡 |
| 一对多 (1:n) |
在**多方(n 方)**建立外键列,指向一方的主键(去掉 UNIQUE 约束) |
一个班级对应多名学生 |
| 多对多 (m:n) |
不能直接在两张表互相加外键,必须引入第三张中间表绑定双方主键 |
用户(账号)与角色 |
二、一对一关联 (1:1)
一对一的核心在于:从表的外键字段既要引用主表主键,又要加上 UNIQUE 约束,从而保证一个主表记录最多只能被一条从表记录引用。
2.1 创建主表 t_card
|
1
2
3
4
5
6
|
-- 1. 创建主表 (t_card)
CREATE TABLE t_card (
card_id INT PRIMARY KEY AUTO_INCREMENT,
card_number VARCHAR(10),
card_date INT
);
|
2.2 创建从表 t_yonghu(外键字段加 UNIQUE)
|
1
2
3
4
5
6
7
|
-- 2. 创建从表 (t_yonghu),暂不绑定外键关系,但外键字段需加 UNIQUE
CREATE TABLE t_yonghu (
yonghu_id INT PRIMARY KEY AUTO_INCREMENT,
yonghu_name VARCHAR(10),
yonghu_address VARCHAR(10),
fk_card_id INT UNIQUE
);
|
2.3 使用 ALTER TABLE 动态追加外键约束
|
1
2
3
|
-- 3. 使用 ALTER TABLE 动态追加外键约束 (约束命名为 fk_1)
ALTER TABLE t_yonghu
ADD CONSTRAINT fk_1 FOREIGN KEY (fk_card_id) REFERENCES t_card(card_id);
|
2.4 插入测试数据
|
1
2
3
4
5
6
|
-- 4. 插入测试数据
INSERT INTO t_card VALUES (NULL, '000000000', 10);
INSERT INTO t_card VALUES (NULL, '111111111', 10);
INSERT INTO t_yonghu VALUES (NULL, 'zhangsan', '西安', 1);
INSERT INTO t_yonghu VALUES (NULL, 'lisi', '北京', 2);
|
关键点:因为 fk_card_id 加了 UNIQUE,所以同一个 card_id 不能被多个用户引用,从而实现一对一关系。
三、一对多关联 (1:n)
3.1 设计原则
- 外键列必须添加到多方这一边。
- 一个班级(一方)对应多名学生(多方),所以外键加在学生表。
- 与一对一的区别:外键列不再加 UNIQUE 约束,从而允许一条主表记录被多条从表记录引用。
3.2 示例代码
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
-- 1. 创建一方表:班级表 (t_class)
CREATE TABLE t_class (
class_id INT PRIMARY KEY AUTO_INCREMENT,
class_name VARCHAR(10),
class_type VARCHAR(10),
class_count INT
);
-- 2. 创建多方表:学生表 (t_student)
-- 建表时直接定义外键 (不加 UNIQUE 约束)
CREATE TABLE t_student (
stu_id INT PRIMARY KEY AUTO_INCREMENT,
stu_name VARCHAR(10),
stu_age INT,
stu_address VARCHAR(20),
fk_class_id INT,
FOREIGN KEY (fk_class_id) REFERENCES t_class(class_id)
);
|
也可以先建表,后续通过 ALTER TABLE 追加外键约束:
|
1
2
3
|
-- 补充写法:先建表后追加外键
ALTER TABLE t_student
ADD CONSTRAINT fk_1 FOREIGN KEY (fk_class_id) REFERENCES t_class(class_id);
|
四、多对多关联 (m:n)
4.1 设计原则
- 两张主表(账号表、角色表)各自保持独立,不加任何外键。
- 必须单独创建一张中间表(关系表),里面包含两个外键,分别指向两张主表的主键。
4.2 示例代码
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
-- 1. 创建账号表 (t_zhanghao)
CREATE TABLE t_zhanghao (
zhanghao_id INT PRIMARY KEY AUTO_INCREMENT,
zhanghao_name VARCHAR(20),
zhanghao_miaoshu VARCHAR(20)
);
-- 2. 创建角色表 (t_juese)
CREATE TABLE t_juese (
juese_id INT PRIMARY KEY AUTO_INCREMENT,
juese_name VARCHAR(20),
juese_miaoshu VARCHAR(20)
);
-- 3. 创建中间表 (t_zhanghao_juese) 负责绑定双方关联
CREATE TABLE t_zhanghao_juese (
id INT PRIMARY KEY AUTO_INCREMENT,
fk_zhanghao_id INT,
fk_juese_id INT
);
-- 4. 为中间表追加两个外键约束
ALTER TABLE t_zhanghao_juese
ADD CONSTRAINT fk_zhanghao FOREIGN KEY (fk_zhanghao_id) REFERENCES t_zhanghao(zhanghao_id);
ALTER TABLE t_zhanghao_juese
ADD CONSTRAINT fk_juese FOREIGN KEY (fk_juese_id) REFERENCES t_juese(juese_id);
|
4.3 插入测试数据
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
-- 5. 插入测试数据
-- 插入账号数据
INSERT INTO t_zhanghao VALUES (NULL, 'zhangsan', '老实人');
INSERT INTO t_zhanghao VALUES (NULL, 'lisi', '伶俐人');
-- 插入角色数据
INSERT INTO t_juese VALUES (NULL, '开发', '写代码');
INSERT INTO t_juese VALUES (NULL, '测试', '找茬');
-- 插入关联关系 (实现 zhangsan 和 lisi 同时拥有开发和测试角色)
INSERT INTO t_zhanghao_juese VALUES (NULL, 1, 1);
INSERT INTO t_zhanghao_juese VALUES (NULL, 1, 2);
INSERT INTO t_zhanghao_juese VALUES (NULL, 2, 1);
INSERT INTO t_zhanghao_juese VALUES (NULL, 2, 2);
|
关键点:多对多的本质是「一个账号可有多个角色,一个角色也可属于多个账号」,这种双向的「多」关系只能通过中间表来承载。
五、多表联合查询(JOIN)
5.1 三种连接类型的区别
在进行多表联查时,通常使用 ON 来指定关联条件,主要分为以下三种连接方式:
| 连接类型 |
关键字 / 语法 |
核心区别(结果集特点) |
| 内连接 |
INNER JOIN 或 JOIN |
只显示左表和右表共同满足关联条件的数据(取交集) |
| 左(外)连接 |
LEFT JOIN 或 LEFT OUTER JOIN |
左表数据全部显示;右表有匹配则显示,没有匹配补 NULL |
| 右(外)连接 |
RIGHT JOIN 或 RIGHT OUTER JOIN |
右表数据全部显示;左表有匹配则显示,没有匹配补 NULL |
5.2 通用语法格式
SQL
|
1
2
3
4
5
|
SELECT 表别名1.列名1, 表别名2.列名2...
FROM 表名称1 表别名1
[INNER JOIN | LEFT JOIN | RIGHT JOIN] 表名称2 表别名2
ON 表别名1.关联列 = 表别名2.关联列
WHERE 筛选条件;
|
5.3 经典实操示例
假设有学生表 t_student(从表)与班级表 t_class(主表),通过 fk_class_id = class_id 建立一对多关联。
示例一:查询学生姓名为 zhangsan 的所有信息
使用 INNER JOIN(也可使用 LEFT JOIN),查询结果包含学生基本信息和班级信息:
|
1
2
3
4
5
6
7
8
9
10
11
|
SELECT
s.stu_id,
s.stu_name,
s.stu_age,
s.stu_address,
c.class_name,
c.class_type
FROM t_student s
INNER JOIN t_class c
ON s.fk_class_id = c.class_id
WHERE s.stu_name = 'zhangsan';
|
示例二:查询班级名称为 java 的所有信息
使用 LEFT JOIN,保障即使该班级暂时没有学生,班级基本信息也能正常查出:
|
1
2
3
4
5
6
7
8
9
10
11
|
SELECT
c.class_id,
c.class_name,
c.class_type,
s.stu_id,
s.stu_name,
s.stu_age
FROM t_class c
LEFT JOIN t_student s
ON c.class_id = s.fk_class_id
WHERE c.class_name = 'java';
|
选用建议:以哪张表为主显示结果,就把那张表放在 LEFT JOIN 的左边。查询「某班级下的
所有学生」时,班级是主体,用左连接可以避免班级为空时被过滤掉。
六、总结
| 关联关系 |
外键位置 |
是否加 UNIQUE |
是否需要中间表 |
| 一对一 |
从表外键列 |
是 |
否 |
| 一对多 |
多方外键列 |
否 |
否 |
| 多对多 |
中间表两个外键 |
否 |
是 |
掌握以上三种表关联关系的设计思路,再配合 INNER JOIN / LEFT JOIN / RIGHT JOIN 灵活选用,基本可以覆盖日常开发中绝大多数多表查询场景。建议在实际项目中:先理清业务实体之间的数量对应关系,再决定外键落在哪一侧、是否引入中间表,最后根据查询主体选择合适的 JOIN 类型。
|