MySQL进阶篇之范式及E-R图

一、范式概述

1.1 什么是范式

范式(Normal Form,NF)是关系数据库设计中的一组规范规则。遵从不同级别的规范,可以设计出冗余度更低、异常更少的关系型数据库表结构。

范式的核心目标是:减少数据冗余、消除插入 / 删除 / 更新异常。

1.2 六种范式

关系数据库共有六种范式,级别依次升高:

范式 全称 核心要求
1NF 第一范式 列不可再分(原子性)
2NF 第二范式 消除非主属性对候选键的部分函数依赖
3NF 第三范式 消除非主属性对候选键的传递函数依赖
BCNF 巴斯 - 科德范式 消除主属性对候选键的部分 / 传递依赖(每个决定因素都是候选键)
4NF 第四范式 消除非平凡多值依赖
5NF 第五范式(完美范式) 消除连接依赖

范式越高,数据冗余越小,但表拆分越多,查询时可能需要更多 JOIN。

1.3 范式与性能的权衡

  • 范式高:冗余少、一致性好、写入异常少,但查询可能需要多表 JOIN,增加查询复杂度和 IO;
  • 范式低(反范式):冗余多、查询快(单表即可),但写入时需要维护多处数据的一致性,存在更新异常风险。

实际应用中,通常满足 3NF 即可;在高并发、读多写少的互联网场景,有时会故意反范式(增加冗余字段)来减少 JOIN、提升查询性能。


二、核心概念铺垫

在讲解范式之前,必须先理解几个关键概念。

2.1 函数依赖

函数依赖是指:在一张表中,如果属性(字段)X 的值确定后,属性 Y 的值也唯一确定,就称 Y 函数依赖于 X ,记作 X → Y。

例如:学生表中,学号确定后,姓名就唯一确定 → 学号 → 姓名。

三种函数依赖
类型 定义 示例
完全函数依赖 X→Y,且 X 的任何真子集都不能决定 Y (学号,课程号) → 成绩(缺了哪个都不行)
部分函数依赖 X→Y,但 X 的某个真子集就能决定 Y (学号,课程号) → 姓名(其实学号 alone 就能决定姓名)
传递函数依赖 X→Y,Y→Z,且 Y 不能反过来决定 X,则 X→Z 是传递依赖 学号→班级号,班级号→班主任姓名 → 学号→班主任姓名(传递)

2.2 候选键、主键、主属性、非主属性

概念 定义
候选键(Candidate Key) 能唯一标识 表中一行记录的最小属性集合。可以是单个字段,也可以是多个字段的组合。一张表可以有多个候选键。
主键(Primary Key) 从候选键中选一个作为行的唯一标识,一张表只能有一个主键。
主属性(Prime Attribute) 出现在任何一个候选键中的属性。
非主属性(Non-prime Attribute) 不出现在任何候选键中的属性。
示例

学生表(学号,身份证号,姓名,年龄):

  • 候选键有两个:{学号} 和 {身份证号}(都能唯一标识一个学生);
  • 选 {学号} 作为主键;
  • 主属性:学号、身份证号(出现在候选键中);
  • 非主属性:姓名、年龄。

三、第一范式(1NF)

3.1 定义

数据库表中的每一列都是不可再分的原子数据项,不能是集合、数组、对象等非原子数据。

换句话说:每一行的每一列,只存一个值,且这个值不能再拆分成更小的有意义的数据。

3.2 反例与正例

反例(不满足 1NF)
学号 姓名 联系方式
001 张三 138xxxx, 025-xxxx

"联系方式" 一列存了手机号和座机号两个值,可以再拆分 → 不满足 1NF。

正例(满足 1NF)
学号 姓名 手机号 座机号
001 张三 138xxxx 025-xxxx

拆成两列,每列只存一个原子值 → 满足 1NF。

3.3 关于 SET 类型

MySQL 的 SET 类型可以在一列中存储多个值(如 '阅读,运动,音乐'),这天然违反 1NF。

但这不代表绝对不能用 SET:如果业务场景中这个字段就是作为一个整体使用、从不单独查询其中某个值,且能在冗余和效率之间做出权衡,SET 仍可使用。


四、第二范式(2NF)

4.1 定义

在满足 1NF 的前提下,消除非主属性对候选键的部分函数依赖。

换句话说:表中每一个非主属性,都必须完全函数依赖于整个候选键,而不能只依赖候选键的一部分。

如果主键是单一字段(非复合主键),则天然满足 2NF------ 因为单一字段不存在 "部分依赖" 的问题。

4.2 部分函数依赖的判定

假设复合主键是 (A, B):

  • 如果某个非主属性 C,只靠 A 就能确定(不需要 B),那 C 就部分依赖于主键 → 不满足 2NF;
  • 如果 C 必须同时靠 A 和 B 才能确定,那 C 完全依赖于主键 → 满足 2NF。

4.3 反例分析

样例 :将所有字段填入一个表,用 (学生id, 班级名称) 作为复合主键。

学生 id 班级名称 学生姓名 学生性别 班级教室 班主任

分析各字段的依赖关系:

  • 学生姓名、学生性别:只靠 学生id 就能确定 → 部分依赖(不需要班级名称);
  • 班级教室、班主任:只靠 班级名称 就能确定 → 部分依赖(不需要学生 id);
  • 没有任何非主属性是完全依赖于整个复合主键的。

→ 不满足 2NF。

4.4 不满足 2NF 导致的异常

异常类型 定义 说明 示例
插入异常 本该可以正常录入的数据,由于缺少主键的组成部分,违反主键非空约束,导致无法插入 想插入一个还没有学生的班级,无法插入(因为学生 id 是主键的一部分,不能为空) 新建了一个班级但还没招生,无法录入班级信息
删除异常 删除某一条数据时,连带删除了本不该删除的其他实体信息,造成非预期的数据丢失 删除某班所有学生时,班级信息也一起被删掉了 某届学生全部毕业后删除学生记录,班级信息也没了
更新异常 修改某一个属性值时,需要同时修改多行数据;只要有一行漏改,就会产生数据不一致 修改班级信息时,需要修改所有该班学生的行,漏改一处就不一致 班级换教室,需要更新该班几十个学生的行
数据冗余 同一个班级的信息在每个学生行中重复存储 50 个学生的班级,班级名称 / 教室 / 班主任重复存 50 遍

核心本质 :不满足第二范式引发的插入、删除、更新异常,根源是将本应独立的多个实体强行揉合进同一张关系表,破坏了 "一表一主题" 的设计原则,使不同实体的数据与操作产生了不必要的强制绑定。往往是m:n关系的实体

范式逻辑对应 : 不满足 2NF 的核心特征,是非主属性对复合候选键存在部分函数依赖:非主属性的实际决定因素只是候选键中的一部分主属性,和候选键里的另一部分主属性并无依赖关系;但在存储层面,该非主属性必须与完整的候选键绑定,共同构成一行数据。


五、第三范式(3NF)

5.1 定义

在满足 2NF 的前提下,消除非主属性对候选键的传递函数依赖。

换句话说:非主属性必须直接依赖于候选键,不能通过其他非主属性间接依赖(传递依赖)。

5.2 传递函数依赖的判定

如果存在 候选键 → A → B,且 A 不能反过来决定候选键(表明A不是候选键),则 B 传递依赖于候选键 → 不满足 3NF。(往往是将两个1:n关系的实体揉合在一张表中)

5.3 反例分析

样例 :如果只用 学生id 作为单一主键:

学生 id 学生姓名 所属班级 班级名称 班级教室 班主任
  • 主键是单一字段 学生id → 天然满足 2NF;
  • 但依赖关系:学生id → 所属班级,所属班级 → 班级名称/教室/班主任;
  • 所以 班级名称/教室/班主任 通过 所属班级 传递依赖 于 学生id → 不满足 3NF。

5.4 满足 3NF 的设计

拆成两张表:

班级表(主表)

班级 id(主键) 班级名称 班级教室 班主任

学生表(从表)

学生 id(主键) 学生姓名 学生性别 所属班级 id(外键)
  • 班级表中,所有字段都直接依赖于 班级id;
  • 学生表中,所有字段都直接依赖于 学生id(所属班级 id 是外键,直接依赖学生 id);
  • 消除了传递依赖 → 满足 3NF。

满足 3NF 后,插入异常、删除异常、更新异常和数据冗余都得到了显著改善。


六、BCNF(巴斯 - 科德范式)

6.1 定义

BCNF 是 3NF 的增强版。在满足 3NF 的前提下,对于任何非平凡的函数依赖 X→Y,X 必须是候选键。

换句话说:每个决定因素(能决定其他属性的属性)都必须是候选键,不允许非候选键的属性去决定其他属性。

6.2 3NF 与 BCNF 的区别

  • 3NF 允许主属性对候选键的部分依赖或传递依赖;
  • BCNF 连主属性的部分 / 传递依赖也消除了。

简单记:3NF 是 "非主属性不传递依赖于候选键",BCNF 是 "所有属性(包括主属性)都不传递 / 部分依赖于候选键"。

6.3 示例

1. 关系模式与业务规则

关系表:STJ(学生编号 S, 教师编号 T, 课程编号 J) 业务规则:

  1. 每位教师只讲授一门固定课程;
  2. 一门课程可以由多位不同的教师讲授;
  3. 每个学生选修某一门课程时,对应一位固定的授课教师;
  4. 学生可以选修多门课程。
2. 函数依赖

根据业务规则推导:

  • T → J:教师编号可以唯一确定课程(一个老师只教一门课)
  • (S, J) → T:学生 + 课程的组合,可以唯一确定授课教师
  • (S, T) → J:学生 + 教师的组合,可以唯一确定对应课程
3. 候选键与主属性
  • 候选键 1:(S, J) ------ 能唯一标识一行记录,且去掉任意一个字段都失去标识能力
  • 候选键 2:(S, T) ------ 能唯一标识一行记录,且去掉任意一个字段都失去标识能力
  • 主属性:S、T、J(三个属性都出现在候选键中,全都是主属性)
  • 非主属性:无
4. 范式判定
  • 满足 3NF:3NF 的约束是「消除非主属性对候选键的部分 / 传递依赖」。本例中没有非主属性,天然满足 3NF 的全部要求。
  • 违反 BCNF :BCNF 的核心规则是「关系中所有决定因素都必须是候选键」。 本例中存在函数依赖 T → J,决定因素是 T,但 T 单独不是候选键(T 只能确定课程 J,无法确定学生 S,不能唯一标识整行记录)。 本质是存在「主属性 J 对 候选键 (S,T) 的部分函数依赖」,因此违反 BCNF。
5. 违反 BCNF 带来的异常
  • 插入异常:新增一位教师并确定其授课课程时,只要还没有学生选修这门课,就无法插入数据(S 是主属性,不能为空)。
  • 删除异常:删除某一名学生的选课记录,会连带删除掉「该教师教授这门课」的关联信息。
  • 更新异常:某教师更换授课课程,所有选该教师课程的学生记录都需要同步修改,漏改就会数据不一致。
6. 修正为 BCNF 的方式

将原表拆分为两张表,消除不合理的依赖:

  1. 教师课程表(T, J),主键:T
  2. 学生选课表(S, T),主键:(S, T) 拆分后两张表的所有决定因素都是候选键,均满足 BCNF。

七、反范式设计

7.1 什么时候需要反范式

范式越高,表拆得越细,查询时 JOIN 越多。在以下场景,有时会故意违反范式、增加冗余:

场景 反范式做法 目的
读多写少、查询频繁 在订单表中冗余存储用户名、商品名 查询订单时不用 JOIN 用户表、商品表
统计报表 预先计算好统计值存入汇总表 避免每次查询都做复杂聚合
历史数据不可变 订单快照保存下单时的商品价格 商品价格变动不影响历史订单

7.2 反范式的代价

  • 数据一致性维护成本高:修改冗余字段时,需要同时更新所有存储该字段的表;
  • 写入性能下降:一次写入可能涉及多张表的更新;
  • 存在不一致风险:如果更新时漏了某处,就会出现数据不一致。

反范式是用空间和一致性维护成本换查询性能,需要根据业务场景权衡,不能盲目反范式。


八、E-R 图

8.1 什么是 E-R 图

E-R 图(Entity-Relationship Diagram,实体 - 关系图)是一种用于描述数据模型的概念图,主要用于数据库设计阶段,帮助理清实体、属性和实体之间的关系。

8.2 基本组成

元素 表示 含义 类比
实体(Entity) 矩形框 数据对象,如学生、班级、课程 类似表
属性(Attribute) 椭圆形 / 圆角矩形 实体的特性,如学生的姓名、年龄 类似字段
关系(Relationship) 菱形框 实体之间的联系,并标注关系类型 类似外键 / 中间表

8.3 关系类型

1. 一对一(1:1)

一个实体 A 的实例对应另一个实体 B 的一个实例,反之亦然。

示例:用户 和 账户

  • 用户属性:昵称、头像、手机号、地址......
  • 账户属性:登录用户名、密码

设计方式:可以在任一实体中保存关联另一实体的字段(外键)。通常把不常用的字段拆分到另一张表,减少主表的宽度。

2. 一对多(1:N)/ 多对一(N:1)

一个实体 A 的实例对应多个实体 B 的实例,但一个实体 B 的实例只对应一个实体 A 的实例。

示例:班级 和 学生(一个班级有多个学生,一个学生只属于一个班级)。

设计方式 :在多的一方 (学生表)保存外键,关联一的一方(班级表)的主键。

3. 多对多(M:N)

一个实体 A 的实例对应多个实体 B 的实例,反之亦然。

示例:学生 和 课程(一个学生选多门课,一门课被多个学生选)。

设计方式 :必须引入中间表(也叫连接表 / 关联表),将多对多拆成两个一对多。

8.4 多对多的中间表设计

以学生选课为例,引入成绩表作为中间表:

  • 一个学生有多个成绩 → 学生:成绩 = 1:N
  • 一个课程有多个成绩 → 课程:成绩 = 1:N

成绩表字段:

字段 说明
id 主键,自增
student_id 外键,关联学生表
course_id 外键,关联课程表
score 成绩

中间表的主键可以是自增 id,也可以用 (student_id, course_id) 作为复合主键(确保一个学生一门课只有一个成绩)。

8.5 EE-R图: 从 SQL 反推 E-R 图

也可以通过已有的建表 SQL 反向生成 E-R 图(逆向工程)。

注意:

  • 工具是通过外键约束来识别表之间的关系的;
  • 如果建表时没有建立外键(只是逻辑上有关联),工具不会自动连线;
  • 因此逆向生成的 E-R 图不一定完全符合设计意图,需要人工核对。

工具推荐:

  • MySQL Workbench(免费,官方工具,支持逆向工程);
  • Navicat(付费,也有 E-R 图功能);
  • draw.io / diagrams.net(免费,手动绘制)。
相关推荐
SelectDB3 小时前
ES 写入拒绝与磁盘暴涨:排查命令、参数调整与改用 Doris 的落地步骤
大数据·数据库·数据分析
此时不提桶,更待何时4 小时前
05-04-B-HBase与时序库面试与生产事故实战
数据库·面试·hbase
Shadow(⊙o⊙)4 小时前
表的增删查改
数据库·mysql
步行cgn4 小时前
Spring 负责注入的注解详解
java·sql·spring
+VX:Fegn08955 小时前
计算机毕业设计|基于springboot + vue外卖点餐系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
FYKJ_20105 小时前
springboot刑事案件管理系统03047-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
盟接之桥6 小时前
线束数字化--先进先出为什么总是停留在纸面上?
大数据·网络·数据库·人工智能·制造·ai编程
FYKJ_20106 小时前
springboot助农产品销售商城05829-计算机课程设计、毕业设计
java·spring boot·python·mysql·spark·django
Kamille Bidan7 小时前
MySQL数据库引擎介绍
mysql