MySQL 复盘多表关联(1对1、1对多、多对多) 帮你打通任督二脉

简介: 本文详解数据库中1对1、1对多、多对多关系。通过用户与身份证、班级与学生、学生与课程等实例,结合SQL建表与查询语句,深入讲解外键、唯一约束、中间表等关键概念,帮助理解数据表间的关联机制及实际应用。

关于1对1、1对多、多对多的操作 前面我们也讲过很多次了!

不过还是有朋友一直没有搞清楚这三种关系,今天这里给大家抛几个案例复习一下!

一对一 关系

MySQL中, 一对一关系两个表通过主键外键关联,并且每条记录在另一表中仅对应唯一记录

如果还没有理解,我们来看一个案例帮助理解~

例如: 用户和身份证-->(一对一关系)

比如说: 这里有两个表,用户表(user)用户信息表(card), 而且每个用户有唯一用户id也就是主键id

这个时候,用户信息表里面的的身份证是不是只能属于一个用户? 对吧!

那么我们可以在这个用户信息表中创建一个外键(user_id)去关联用户表

SQL语句如下

/*用户表(主表)*/
CREATE TABLE user (
    id VARCHAR(32) PRIMARY KEY,  -- 用户唯一ID(如U1001)
    username VARCHAR(30)         -- 用户名(如 张三)
);

/*用户信息表*/
CREATE TABLE id_card (
    id VARCHAR(32) PRIMARY KEY,      -- 身份证唯一ID(如 "C2001")仅仅代表数据条目的唯一性
    card_number VARCHAR(18) UNIQUE,  -- 身份证号(实际业务中可能也是唯一键,这里强调关联唯一)
    user_id VARCHAR(32) UNIQUE,      -- 关联的用户ID(必须唯一!)
    issue_date DATE,                 -- 发证日期
    CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES user(id)
);

我们把代码复制到Navicat for MySQL中去执行一下,

如图

这个时候,可能有人要问了,我这样创建出来的表,在从表(id_card)中, 可以添加多个不同的身份证去关联一个用户吗? 那么这样就会形成一个一对多(1:N)的关系了! 显然这样是不对的~

那么为了强制形成1:1关系 我们可以在从表的相应字段上加入UNIQUE约束

根据上面的案例,我们可以看到user_id字段既是外键 又是唯一键

并且card_number字段也做了UNIQUE约束处理, 因为身份证不能重复!

确保一个用户只能被一个身份证关,从而强制形成1:1关系

如图

从表的user_id字段必须填写并指向主表user.id字段, 不能乱填!

并且user_id字段加了 UNIQUE约束,确保一个用户, 如U1001只能被一张身份证, 如C2001所关联!

反过来,也是一样的, 一张身份证也只能关联一个用户,不能同时属于多个用户!

现实场景中,一个人只能有一张有效身份证,一张身份证也只能属于一个人,这是法律规定的1:1关系!

所以有的时候,我们要搞明白对应关系,你就要对现实场景做出正确的分析才可以~

如果我们不在相应字段上处理或者不加UNIQUE, 那么则默认是一对多(1:N)

比如这里我们取消user_idcard_number字段的UNIQUE约束看看会怎么样~!

现在我们再次添加数据到从表(id_card)中看看~

如图

看到问题所在了吧~显然这样做是不符合逻辑的!

要加UNIQUE后, 严格满足我们数据库中的第三范式1:1关系的定义!

总结

1:1 关系 = 从表的外键字段要加 UNIQUE 强制形成1 : 1

1:N 关系 = 从表的外键字段不加 UNIQUE 这样允许重复关联!

很多人没有搞懂1:1 关系1:N 关系 的本质区, 这样是不是更清楚了?

案例

一个老公对应一个老婆,一个老婆对应一个老公,互相唯一绑定!

SQL代码

/*老公表*/
CREATE TABLE man(
  id VARCHAR(32) PRIMARY KEY,
  username VARCHAR(30)
);

/*老婆表*/
CREATE TABLE woman(
  id VARCHAR(32) PRIMARY KEY,
  username VARCHAR(30),
  husband VARCHAR(32) UNIQUE,
  CONSTRAINT wm_fk FOREIGN KEY(husband) REFERENCES man(id)
);

插入数据

INSERT INTO man VALUES('1','小明');
INSERT INTO man VALUES('2','小聪');
INSERT INTO man VALUES('3','老王');

INSERT INTO woman VALUES('1','小花','2');
INSERT INTO woman VALUES('2','小静','1');

那么如果我们再次插入以下数据就是错误的

INSERT INTO woman VALUES('3','小红','1');   //错误:因为违反1对1
INSERT INTO woman VALUES('3','小红','10');  //错误:因为违反外键--主表必须存在该外键值才行

接下来,我们就可以使用select查询语句来进行关联查询一下夫妻信息, 这里我们直接使用内联查询即可

SQL如下

SELECT m.username as '老公名称',w.username as '老婆名称'
FROM man as m INNER JOIN woman as w
ON m.id = w.husband
ORDER BY m.id DESC

结果如下

一对多 关系

搞懂了一对一的关系,那么一对多的关系也很好理解了!

一对一的关系也就是一个主表记录可以对应多个从表记录,但从表记录只能属于一个主表记录!

不明白没关系,我们来结合案例理解就行了!

举个栗子: 班级和学生

现在有一个班级表(class) 和 一个学生表(student)

那么现实生活中,我们已经知道一个班级可以有多个学生, 比如有小明、小红...

但每个学生只能属于一个班级, 对吧! 这就是这就是1个班级 : N个学生 也就是1:N的关系

所以根据这个逻辑关系,我们可以把这两个表的代码结构创建出来

SQL如下

/*主表:班级(class)*/
CREATE TABLE class (
    id INT PRIMARY KEY,      -- 班级表ID(如1班)
    username VARCHAR(20)     -- 班级名称
);

/*从表:学生(student)*/
CREATE TABLE student (
    id INT PRIMARY KEY,  -- 学生表ID
    username VARCHAR(20),-- 学生姓名
    class_id INT,        -- 外键,关联班级ID 并且这个字段不加UNIQUE!
    FOREIGN KEY (class_id) REFERENCES class(id)  
);

从创建表结构上看,这里也看出来了区别~ 关联的字段没有加入UNIQUE约束!

因为从场景关系中分析我们得出的结论就是 主表一条记录可以对应从表多条记录, 所以不要加UNIQUE约束

总之记住一点: 主表的一条记录能拥有从表N条记录,但从表每条记录只能属于主表的一条记录!

而且还要注意一点,外键是建立在多的一方,也就是从表里面!

案例

比如人和车的关系,在现实中,我们一个人是不是可以购买多辆车呢!

那按照这个场景逻辑,我们创建表结构如下

/*主表 用户表*/
CREATE TABLE Person(
  id VARCHAR(32) PRIMARY KEY,
  username VARCHAR(30),
  sex VARCHAR(5)
);

/*从表 车辆表*/
CREATE TABLE car(
  id VARCHAR(32) PRIMARY KEY, /*车辆表主键字段*/
  username VARCHAR(30),          /* 车辆名称*/
  price NUMERIC(10,2),           /*车辆价格*/
  pid VARCHAR(32),              /* 车辆关联字段 代表车辆属于谁*/
  CONSTRAINT car_fk FOREIGN KEY(pid) REFERENCES Person(id)
);

插入数据

INSERT INTO Person VALUES('P01','杰克','男');
INSERT INTO Person VALUES('P02','汤姆','男');
INSERT INTO Person VALUES('P03','爱丽丝','女');
INSERT INTO Person VALUES('P04','巴顿','男');

INSERT INTO car VALUES('C001','宝马',300000,'P01');
INSERT INTO car VALUES('C002','奔驰',400000,'P01');
INSERT INTO car VALUES('C003','奥迪',450000,'P01');
INSERT INTO car VALUES('C004','QQ车',55000,'P02');

INSERT INTO car VALUES('C005','特斯拉',188000,null);
INSERT INTO car VALUES('C006','小米Su7',311000,null);
INSERT INTO car VALUES('C007','奔驰',400000,'P03');
INSERT INTO car VALUES('C008','宝马',300000,'P03');

注意: pid外键字段值可以为NULL, 来表示该车辆还未卖出!

那么现在我们就来查询一下!

问题1: 查询哪些人购买了哪些车

SQL如下

SELECT p.username as '姓名', c.username as '车辆名称'
FROM person as p INNER JOIN car as c
ON p.id = c.pid

如图

问题2: 查询杰克拥有哪些车

SQL如下

SELECT p.username as '姓名', c.username as '车辆名称'
FROM person as p INNER JOIN car as c
ON p.id = c.pid
WHERE p.username='杰克'

如图

问题3: 查询哪些人有两辆或两辆以上的车

SQL如下

SELECT p.username as '姓名'
FROM person as p INNER JOIN car as c
ON p.id = c.pid
GROUP BY p.id
HAVING COUNT(c.username)>2


/*或者*/

SELECT p.username as '姓名', c.username as '车辆名称',c.price as '车辆价格'
FROM person as p INNER JOIN car as c
ON p.id = c.pid
WHERE p.id in (SELECT pid FROM car GROUP BY pid HAVING COUNT(pid)>=2)

如图

问题4:查询谁没有车

SQL如下

SELECT person.username FROM person 
LEFT JOIN car ON car.pid=person.id WHERE pid is NULL

如图

右关联跟左关联一样,只需要把左关联的表调换一下位置便成了右关联的结果,

所以只要会了左关联,右关联也是一样的!

注意:

删除主键信息时,如果该主键字段值在外键表中存在时,该记录是不能删除的

我们要把外表相关信息删除之后,才能删除!

平时我们在做开发和练习的时候,也一定要多结合官方的文档~

多对多 关系

如果你搞懂了 前面两种表关系,那么多对多也可以很简单理解!

多对多(M:N) 也就是两个表的记录可以互相多对多的进行关联, 但必须通过第三张中间表来带两个外键实现!

举个栗子

比如学生选课, 现实中,一个学生可选多门课,一门课也可被多个学生选!

根据这个逻辑关系,我们创建以下3个表

SQL语句如下

-- 学生表
CREATE TABLE student (
    id INT PRIMARY KEY,
    sname VARCHAR(20)
);

-- 课程表
CREATE TABLE course (
    id INT PRIMARY KEY,
    title VARCHAR(50)
);

-- 中间表(解决多对多)
CREATE TABLE student_course (
    student_id INT,
    course_id INT,
    PRIMARY KEY (student_id, course_id),  -- 联合主键防止重复选课
    FOREIGN KEY (student_id) REFERENCES student(id),
    FOREIGN KEY (course_id) REFERENCES course(id)
);

添加数据

-- 插入学生数据
INSERT INTO student (id, sname) VALUES (1, '张三'),(2, '李四'),(3, '王五');

-- 插入课程数据
INSERT INTO course (id, title) VALUES (101, '数学'),(102, '英语'),(103, '编程');


-- 插入选课关系(中间表)
INSERT INTO student_course (student_id, course_id) VALUES 
(1, 101), -- 张三选数学
(1, 102), -- 张三选英语
(2, 101), -- 李四选数学
(2, 103), -- 李四选编程
(3, 102); -- 王五选英语

那么现在我们就可以来查询一下多对多关系的数据了!

问题1: 查询哪些人选了哪些课

SELECT s.sname,c.title FROM student as s,course as c,student_course as sc
WHERE sc.student_id=s.id and sc.course_id=c.id

/*或者*/

SELECT s.sname,c.title FROM student as s 
INNER JOIN student_course as sc ON s.id = sc.student_id
INNER JOIN course as c  ON c.id = sc.course_id

如图

问题2 查询哪些人没有选课

SELECT s.sname as '姓名',c.title as '课程名' FROM student as s 
LEFT  JOIN student_course as sc ON s.id = sc.student_id
LEFT  JOIN course as c  ON c.id = sc.course_id
WHERE c.title is NULL;

如图

问题3 反过来查询哪些课没人选

SELECT s.sname as '姓名',c.title as '课程名' FROM course as c 
LEFT  JOIN student_course as sc ON c.id = sc.course_id
LEFT  JOIN student as s  ON s.id = sc.student_id
WHERE s.sname is NULL

如图

多对多的核心重点其实就在于中间表, 用它来记录另外两个表之间的关系,并且通过两个外键关联两张主表!

上面的案例中就很好的体现了这一点,张三选了数学英语(1:N)而数学被张三、李四选(N:1)

整体形成学生↔课程多对多(M:N)关系,通过中间表实现交叉关联!

相关文章
|
4天前
|
存储 JavaScript 前端开发
JavaScript基础
本节讲解JavaScript基础核心知识:涵盖值类型与引用类型区别、typeof检测类型及局限性、===与==差异及应用场景、内置函数与对象、原型链五规则、属性查找机制、instanceof原理,以及this指向和箭头函数中this的绑定时机。重点突出类型判断、原型继承与this机制,助力深入理解JS面向对象机制。(238字)
|
3天前
|
云安全 人工智能 安全
阿里云2026云上安全健康体检正式开启
新年启程,来为云上环境做一次“深度体检”
1548 6
|
5天前
|
安全 数据可视化 网络安全
安全无小事|阿里云先知众测,为企业筑牢防线
专为企业打造的漏洞信息收集平台
1322 2
|
5天前
|
缓存 算法 关系型数据库
深入浅出分布式 ID 生成方案:从原理到业界主流实现
本文深入探讨分布式ID的生成原理与主流解决方案,解析百度UidGenerator、滴滴TinyID及美团Leaf的核心设计,涵盖Snowflake算法、号段模式与双Buffer优化,助你掌握高并发下全局唯一ID的实现精髓。
337 160
|
5天前
|
人工智能 自然语言处理 API
n8n:流程自动化、智能化利器
流程自动化助你在重复的业务流程中节省时间,可通过自然语言直接创建工作流啦。
397 6
n8n:流程自动化、智能化利器
|
7天前
|
人工智能 API 开发工具
Skills比MCP更重要?更省钱的多!Python大佬这观点老金测了一周终于懂了
加我进AI学习群,公众号右下角“联系方式”。文末有老金开源知识库·全免费。本文详解Claude Skills为何比MCP更轻量高效:极简配置、按需加载、省90% token,适合多数场景。MCP仍适用于复杂集成,但日常任务首选Skills。推荐先用SKILL.md解决,再考虑协议。附实测对比与配置建议,助你提升效率,节省精力。关注老金,一起玩转AI工具。
|
14天前
|
机器学习/深度学习 安全 API
MAI-UI 开源:通用 GUI 智能体基座登顶 SOTA!
MAI-UI是通义实验室推出的全尺寸GUI智能体基座模型,原生集成用户交互、MCP工具调用与端云协同能力。支持跨App操作、模糊语义理解与主动提问澄清,通过大规模在线强化学习实现复杂任务自动化,在出行、办公等高频场景中表现卓越,已登顶ScreenSpot-Pro、MobileWorld等多项SOTA评测。
1522 7
|
4天前
|
Linux 数据库
Linux 环境 Polardb-X 数据库 单机版 rpm 包 安装教程
本文介绍在CentOS 7.9环境下安装PolarDB-X单机版数据库的完整流程,涵盖系统环境准备、本地Yum源配置、RPM包安装、用户与目录初始化、依赖库解决、数据库启动及客户端连接等步骤,助您快速部署运行PolarDB-X。
244 1
Linux 环境 Polardb-X 数据库 单机版 rpm 包 安装教程
|
8天前
|
人工智能 前端开发 API
Google发布50页AI Agent白皮书,老金帮你提炼10个核心要点
老金分享Google最新AI Agent指南:让AI从“动嘴”到“动手”。Agent=大脑(模型)+手(工具)+协调系统,可自主完成任务。通过ReAct模式、多Agent协作与RAG等技术,实现真正自动化。入门推荐LangChain,文末附开源知识库链接。
662 119