数据库设计:范式与实操流程
自用复习版
目录
- 一、数据库设计的核心------范式
- [1.1 第一范式(1NF):列的原子性](#1.1 第一范式(1NF):列的原子性)
- [1.2 第二范式(2NF):消除部分函数依赖](#1.2 第二范式(2NF):消除部分函数依赖)
- [1.3 第三范式(3NF):消除传递函数依赖](#1.3 第三范式(3NF):消除传递函数依赖)
- 范式小总结
- 二、数据库设计完整实操流程
- 三、E-R图基础与实体关系处理
- [3.1 E-R图三要素](#3.1 E-R图三要素)
- [3.2 一对一关系处理](#3.2 一对一关系处理)
- [3.3 一对多关系处理](#3.3 一对多关系处理)
- [3.4 多对多关系处理](#3.4 多对多关系处理)
- 四、自用复习小tips
一、数据库设计的核心------范式
范式是关系型数据库设计的通用规则 ,核心目的是减少数据冗余 、避免增删改查的异常问题 。目前有6种范式,实际开发中做到第三范式(3NF) 就完全满足大部分业务需求,也是我们学习的核心。
所有高等级范式的前提:必须先满足低等级范式(比如2NF基于1NF,3NF基于2NF)。
1.1 第一范式(1NF):列的原子性
核心要求 :表中的每一列都必须是不可再分的基本数据类型 ,列的值不能拆成多个子值,这是数据库设计的基础要求 。
简单说:一列只能存一个含义的信息。
举例子
❌ 不满足1NF的表(学生表)
| 学号 | 姓名 | 联系方式 |
|---|---|---|
| 001 | 张三 | 138xxxx1234、zhangsan@qq.com |
原因:联系方式列可以拆分成手机号 和邮箱,列值可再分,违反原子性。 |
✅ 满足1NF的表(学生表)
| 学号 | 姓名 | 手机号 | 邮箱 |
|---|---|---|---|
| 001 | 张三 | 138xxxx1234 | zhangsan@qq.com |
| 原因:每一列都是不可再分的基本类型,一个列只存一个信息。 |
1.2 第二范式(2NF):消除部分函数依赖
前提 :表已经满足第一范式(1NF)
核心要求 :如果表使用复合主键 (多个字段组成的主键,也叫候选键),那么非主键列必须完全依赖整个复合主键 ,不能只依赖复合主键中的某一个字段(这种情况叫部分函数依赖)。
简单说:复合主键的表,非主键列要"缺一不可"的依赖主键整体,不能只认主键的其中一个字段。
举例子(经典学生成绩表)
❌ 不满足2NF的表 (学生成绩表),复合主键:学号+课程号
| 学号 | 课程号 | 姓名 | 课程名 | 成绩 |
|---|---|---|---|---|
| 001 | 数学01 | 张三 | 小学数学 | 90 |
| 001 | 语文01 | 张三 | 小学语文 | 85 |
| 002 | 数学01 | 李四 | 小学数学 | 95 |
问题分析:
- 姓名:只依赖
学号(知道学号001就知道是张三,和课程号无关)→ 部分依赖 - 课程名:只依赖
课程号(知道课程号数学01就知道是小学数学,和学号无关)→ 部分依赖 - 成绩:依赖
学号+课程号(必须同时知道两个字段才知道分数)→ 完全依赖
不满足2NF的4个问题:
- 数据冗余:姓名、课程名重复存储(比如张三出现2次,小学数学出现2次);
- 更新异常:修改课程名"小学数学"为"小学数科",需要改所有课程号为数学01的记录,漏改就会数据不一致;
- 插入异常:想新增一门课程"英语01-小学英语",但暂时没有学生选,主键
学号为空,无法插入; - 删除异常:删除学号001的所有成绩,会连带着把"张三"这个学生信息一起删掉。
✅ 满足2NF的解决方法:拆分表
将原表按实体独立的原则拆成3个表,消除部分依赖:
- 学生表(主键:学号):| 学号 | 姓名 |
- 课程表(主键:课程号):| 课程号 | 课程名 |
- 成绩表(复合主键:学号+课程号):| 学号 | 课程号 | 成绩 |
如果一个表用多个字段凑成的复合主键 (比如 "学号 + 课程号"),那表里除了主键之外的其他列(比如成绩、姓名这些),必须得靠整个复合主键一起才能确定 ------ 不能只靠主键里的某一个字段(比如只靠学号就能确定姓名,只靠课程号就能确定课程名)
拆分后每个表的非主键列都完全依赖自身主键,彻底解决增删改异常和数据冗余问题。
1.3 第三范式(3NF):消除传递函数依赖
前提 :表已经满足第二范式(2NF)
核心要求 :非主键列之间不能存在传递依赖 ,非主键列必须直接依赖主键,而不能通过其他非主键列间接依赖主键。
简单说:表中只能有主键→非主键列 的直接关系,不能有主键→非主键列A→非主键列B的间接关系。
举例子
❌ 不满足3NF的表(学生表),主键:学号
| 学号 | 姓名 | 班级号 | 班级班主任 |
|---|---|---|---|
| 001 | 张三 | 一班 | 王老师 |
| 002 | 李四 | 一班 | 王老师 |
| 003 | 王五 | 二班 | 李老师 |
问题分析:
- 姓名、班级号:直接依赖学号 → 符合要求
- 班级班主任:依赖班级号 ,而非直接依赖学号,形成传递依赖:
学号→班级号→班级班主任→ 违反3NF
不满足3NF的问题 :
和2NF类似,会出现数据冗余(王老师重复存储)、更新异常(改一班班主任为张老师,需修改所有一班学生记录)、插入异常(新建三班无学生,无法插入班级号和班主任)、删除异常(删除二班最后一个学生,连班主任李老师信息一起删掉)。
✅ 满足3NF的解决方法:拆分表
将传递依赖的字段拆成独立的实体表,消除间接依赖:
- 学生表(主键:学号):| 学号 | 姓名 | 班级号 |
- 班级表(主键:班级号):| 班级号 | 班级班主任 |
拆分后,学生表的非主键列直接依赖主键,班级表的属性也贴合自身实体,完全满足3NF。
范式小总结
- 1NF:列不可再分(原子性)→ 基础;
- 2NF:基于1NF,消除部分函数依赖(复合主键专用)→ 实体独立;
- 3NF:基于2NF,消除传递函数依赖 → 无间接依赖;
- 实用技巧:拆分表后,可选择性加少量冗余列 (反范式设计),比如订单表中加商品名称,避免频繁联表查询,兼顾规范性 和查询效率。
二、数据库设计完整实操流程
范式是设计规则,而实际开发中需要按业务梳理→关系设计→建表的流程来做,三步就能完成基础数据库设计,适合新手入门:
步骤1:梳理业务,提取概念类(实体+属性)
从实际业务中找出核心实体 (业务中的关键对象,对应数据库的表),再提取每个实体的属性(实体的特征,对应数据库的列)。
- 实体:比如学生管理系统的「学生」「课程」「班级」;电商系统的「用户」「商品」「订单」;
- 属性:比如「学生」的属性是学号、姓名、班级号;「课程」的属性是课程号、课程名、学分。
步骤2:确定实体间的关系,绘制E-R图
梳理清楚实体之间的关联关系(一对一/一对多/多对多),然后画出E-R图(实体关系图),把「实体、属性、关系」可视化,这是设计的核心环节,能避免后续建表混乱。
步骤3:依据E-R图,结合范式创建数据库表
按照E-R图的内容,将实体拆成独立的表,同时遵循3NF设计规则,处理好实体间的关联字段(外键),最终生成数据库表结构。
三、E-R图基础与实体关系处理
E-R图是数据库设计的可视化工具,核心是把业务转化为直观的图形,方便后续建表,也是面试常考的基础点。
3.1 E-R图三要素
- 实体 :用矩形表示,写实体名称(比如学生、课程);
- 属性 :用椭圆形表示,写属性名称,用线和对应实体连接(比如学号是学生的属性);
- 关系 :用菱形表示,写关系名称,用线和关联的实体连接,线上标注关系类型(一对一/一对多/多对多)。
3.2 一对一关系处理
定义 :A实体的一个记录,只能对应B实体的一个记录,反之亦然。
例子 :学生 ↔ 学生档案(一个学生只有一个档案,一个档案只属于一个学生)。
实操方法:
- 分别为两个实体创建独立的表;
- 在其中一个表 中添加另一个表的主键作为外键(比如在「学生档案表」中加「学号」,关联学生表)。
3.3 一对多关系处理
定义 :A实体的一个记录,可以对应B实体的多个记录,而B实体的一个记录只能对应A实体的一个记录(最常见的关系)。
例子 :班级 → 学生(一个班级有多个学生,一个学生只属于一个班级);商品 → 订单详情(一个商品出现在多个订单详情,一个订单详情只对应一个商品)。
实操方法:
- 分别为两个实体创建独立的表;
- 在**「多」的一方的表中,添加「一」的一方**的主键作为外键(比如在「学生表」中加「班级号」,关联班级表)。
3.4 多对多关系处理
定义 :A实体的一个记录,可以对应B实体的多个记录,B实体的一个记录也可以对应A实体的多个记录。
例子 :学生 ↔ 课程(一个学生选多门课,一门课被多个学生选);用户 ↔ 商品(一个用户收藏多个商品,一个商品被多个用户收藏)。
实操方法:
- 分别为两个实体创建独立的实体表;
- 新建一个中间关系表 (核心),表中包含两个实体表的主键作为复合外键,还可以添加关联属性;
- 例子:学生和课程的中间表是「成绩表」,包含学号(外键)、课程号(外键),还加了「成绩」这个关联属性。
四、自用复习小tips
- 范式的核心是拆分:把有冗余、有依赖的表拆成独立的实体表,记住"1NF原子性、2NF消部分依赖、3NF消传递依赖"即可;
- 3NF是通用标准:不用刻意追求更高阶的范式(4NF/5NF),实际业务中3NF足够,过度设计会增加联表查询的复杂度;
- 实体关系的核心:一对多靠外键,多对多靠中间表,一对一灵活加外键;
- E-R图是桥梁:把业务语言转化为数据库语言,画好E-R图,建表就不会乱。