画师:竹取工坊
大佬们好!我是Mem0rin!现在正在准备自学转码。
如果我的文章对你有帮助的话,欢迎关注我的主页Mem0rin,一起进步!
文章目录
- [MySQL 表设计](#MySQL 表设计)
-
- 一、表设计的基本流程
- 二、范式
-
- (一)第一范式(1NF)
-
- [1. 基本要求](#1. 基本要求)
- [2. 拆分示例](#2. 拆分示例)
- (二)第二范式(2NF)
-
- [1. 基本要求](#1. 基本要求)
- [2. 反例分析](#2. 反例分析)
- [3. 拆分方法](#3. 拆分方法)
- (三)第三范式(3NF)
-
- [1. 基本要求](#1. 基本要求)
- [2. 拆分方法](#2. 拆分方法)
- 三、实体之间的关系
- 四、建表示例
MySQL 表设计
表设计的目标是把业务对象及其关系转换为清晰、稳定的数据库结构,同时减少数据冗余和增删改异常。
一、表设计的基本流程
(一)设计步骤
- 从需求中提取实体,如学生、班级和课程,每个实体通常对应一张表。
- 确定实体的属性,属性通常对应表中的字段。
- 确定实体之间的一对一、一对多或多对多关系。
- 根据范式检查表结构,减少重复数据和依赖问题。
- 使用 SQL 创建表,并设置主键、外键等约束。
(二)通用建表语法
sql
-- 创建数据表
CREATE TABLE table_name (
column1 data_type constraint,
column2 data_type constraint,
PRIMARY KEY (column1),
FOREIGN KEY (column2) REFERENCES other_table (column2)
);
二、范式
范式用于规范表中字段之间的依赖关系;一对一、一对多和多对多用于描述实体之间的数量关系,两者不是同一概念。
常见范式包括第一范式(1NF)、第二范式(2NF)、第三范式(3NF)和 BC 范式(BCNF)。实际设计中通常先掌握前三种范式。
(一)第一范式(1NF)
1. 基本要求
第一范式要求每个字段保存一个不可再分的值,不能在一个字段中存放多个重复项或组合信息。
例如,下面的学生表符合第一范式:
| 学号 | 姓名 | 年龄 | 班级名称 |
|---|---|---|---|
| 10001 | 张三 | 20 | 101班 |
如果把学校名称、地址和电话全部放入一个"学校信息"字段,后续查询和修改会比较困难,应将它们拆分为独立字段。
2. 拆分示例
| 不推荐的字段 | 拆分后的字段 |
|---|---|
| 学校信息 | 学校名称、学校地址、学校电话 |
| 联系方式 | 手机号、电子邮箱 |
"不可再分"需要结合业务判断。例如完整地址在某些业务中可以作为一个字段,在需要按省市统计时则应继续拆分。
(二)第二范式(2NF)
1. 基本要求
第二范式要求:在满足第一范式的基础上,每个非主属性都必须完全依赖于候选键,不能只依赖复合候选键的一部分。
- 候选键是能够唯一标识一条记录的最小字段集合。
- 非主属性是不属于任何候选键的字段。
- 部分依赖通常出现在候选键由多个字段组成的表中。
如果一张表的所有候选键都只有一个字段,它会自动满足第二范式;仅仅"没有复合主键"并不能保证一定满足第二范式,因为还可能存在其他复合候选键。
2. 反例分析
假设成绩表包含以下字段:
| 学号 | 学生姓名 | 年龄 | 课程编号 | 课程名称 | 学分 | 成绩 |
|---|
其中,学号 + 课程编号 可以共同确定一条成绩记录,但各字段的依赖关系不同:
- 学生姓名、年龄只依赖学号。
- 课程名称、学分只依赖课程编号。
- 成绩依赖学号和课程编号的组合。
部分字段只依赖复合键的一部分,因此该表不满足第二范式。
3. 拆分方法
| 表 | 字段 |
|---|---|
| 学生表 | 学号、姓名、年龄 |
| 课程表 | 课程编号、课程名称、学分 |
| 成绩表 | 学号、课程编号、成绩 |
拆分后可以减少以下问题:
- 数据冗余:学生信息和课程信息不再重复保存。
- 更新异常:修改课程学分时只需更新课程表。
- 插入异常:还没有成绩时也能先录入学生和课程。
- 删除异常:删除成绩不会连带丢失学生或课程信息。
(三)第三范式(3NF)
1. 基本要求
第三范式要求:在满足第二范式的基础上,非主属性不能通过另一个非主属性间接依赖候选键,即不能存在传递依赖。
例如,学生表包含以下字段:
| 学号 | 姓名 | 年龄 | 学院编号 | 学院名称 | 学院电话 | 学院地址 |
|---|
依赖关系为:
学号 → 学院编号 → 学院名称、学院电话、学院地址
学院信息通过学院编号间接依赖学号,形成传递依赖。
2. 拆分方法
| 表 | 字段 |
|---|---|
| 学院表 | 学院编号、学院名称、学院电话、学院地址 |
| 学生表 | 学号、姓名、年龄、学院编号 |
学生表只保留学院编号,并通过外键关联学院表。这样修改学院信息时,不需要更新每一条学生记录。
三、实体之间的关系
(一)一对一关系
一条记录最多对应另一张表中的一条记录,例如账号与用户详细信息。
常见设计方式有两种:
- 信息较少且生命周期相同时,合并到一张表。
- 信息较多或职责不同,拆分为两张表,并在外键上添加
UNIQUE约束。
账号系统通常可以先根据账号和密码找到用户编号,再通过用户编号查询详细信息。
(二)一对多关系
一个班级可以有多名学生,但一名学生只属于一个班级。设计时把"一"方的主键放到"多"方作为外键。
| 表 | 主要字段 |
|---|---|
| 班级表 | class_id、class_name |
| 学生表 | student_id、name、class_id |
(三)多对多关系
一名学生可以选择多门课程,一门课程也可以被多名学生选择。多对多关系需要增加中间表。
| 表 | 主要字段 |
|---|---|
| 学生表 | student_id、name |
| 课程表 | course_id、course_name |
| 成绩表 | student_id、course_id、score |
成绩表中的两个外键连接学生表和课程表,二者也可以组成复合主键,避免同一名学生重复添加同一门课程。
四、建表示例
(一)班级表与学生表
sql
-- 创建班级表
CREATE TABLE class (
class_id BIGINT PRIMARY KEY AUTO_INCREMENT,
class_name VARCHAR(20) NOT NULL
);
-- 创建学生表,班级与学生是一对多关系
CREATE TABLE student (
student_id BIGINT PRIMARY KEY AUTO_INCREMENT,
sn VARCHAR(20) NOT NULL UNIQUE,
name VARCHAR(50) NOT NULL,
age INT,
mail VARCHAR(50),
class_id BIGINT,
FOREIGN KEY (class_id) REFERENCES class (class_id)
);
(二)课程表与成绩表
sql
-- 创建课程表
CREATE TABLE course (
course_id BIGINT PRIMARY KEY AUTO_INCREMENT,
course_name VARCHAR(50) NOT NULL,
credit DECIMAL(3, 1) NOT NULL
);
-- 创建成绩表,连接学生表与课程表
CREATE TABLE score (
student_id BIGINT NOT NULL,
course_id BIGINT NOT NULL,
score DECIMAL(5, 2),
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES student (student_id),
FOREIGN KEY (course_id) REFERENCES course (course_id)
);
