MySQL表设计(三大范式 表的设计)
1.上讲约束复习:
1.NOT NULL 非空约束,被指定NOT NULL的列,值不允许为空(必填)
2. UNIQUE 唯一约束,这个列里的值在表中是唯一的,也就是说不能重复
3. PRIMARY KEY 主键约束,可以看做是NOT NULL和UNIQUE的组合可以用一个bigint类型列为表单独指定主键,同时也可以指定主键自增,用auto increment关键字自增操作时,会找到当前列中最大的值,然后在这个基础上加1(+1)
4.DEFAULT 默认约束,如果写入一行记录的时候,指定了默认约束的列,如果在写入数据时没有指定一个确定的值就会用默认值进行填充该字段的值
5.FOREIGN KEY 外键约束,一张表要与另一张表的主键或唯一键进行关键,说明表与表之间的关联关系
2.三大范式
1.范式
数据库的范式是⼀组规则。在设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数 据库,这些不同的规范要求被称为不同的范式。
关系数据库有六种范式:第⼀范式(1NF)、第⼆范式(2NF)、第三范式(3NF)、巴斯-科德 范式(BCNF)、第四范式(4NF)和第五范式(5NF,⼜称完美范式),越⾼的范式数据库冗余越 ⼩。然⽽,普遍认为范式越⾼虽然对数据关系有更好的约束性,但也可能导致数据库IO更繁忙,因此 在实际应⽤中,数据库设计通常只需满⾜第三范式即可。
2.第一范式 确保每个字段的原子性
定义:
• 数据库表的每⼀列都是不可分割的原⼦数据项,⽽不能是集合,数组,对象等⾮原⼦数据。
• 在关系型数据库的设计中,满⾜第⼀范式是对关系模式的基本要求。不满⾜第⼀范式的数据库就不能被称为关系数据库
要求:
- 每个字段只能包含一个值(没有重复组)。
- 表中的每一行应当是唯一的,可以通过主键进行标识。
使用:
第一范式要求每个字段(列)只能有一个值,不能是多个值的集合。
不符合第一范式的设计:
假设我们一开始设计了一个学生表,课程
字段存储了多个课程:
插入数据时,可以看到 courses
列存储了多个课程:
我们可以看出这个设计存在问题,courses
列可以储存多个值,违反了第一范式。我们应该将每个课程单独存储。
符合第一范式的设计:
为了遵循第一范式,我们需要将课程数据拆成每个学生每门课程一行:
学生表:
课程表:
然后插入数据:
这样,每个课程都拆成了单独的一行,符合第一范式。
第二范式(2NF):消除部分依赖
定义:
在满⾜第⼀范式的基础上,不存在⾮关键字段对任意候选键的部分函数依赖。存在于表中定义了复合 主键的情况下。
候选键:可以唯⼀标识⼀⾏数据的列或列的组合,可以从候选键中选⼀个或多个当做表的主键
要求:
- 表必须符合第一范式(1NF)。
- 所有非主键字段必须完全依赖于主键,而不是依赖于主键的一部分。
- 部分依赖是指,当主键由多个字段组成时,某些非主键字段仅依赖于主键的其中一部分,而不是整个主键。
-
问题:
当我们使用复合主键时,如果某些列只依赖于主键的一部分而不是整个复合主键,那么这种依赖就叫做“部分依赖”,这违反了第二范式。
不符合第二范式的设计:
假设我们设计了一个订单表,其中 订单号
和 商品ID
组成复合主键:
假设插入以下数据:
解释:
- 主键:
(order_id, product_id)
,这个复合主键的意思是每个订单(order_id
)包含多个商品(product_id
),每个商品的数量(quantity
)将会是订单明细的一部分。 - 然而,
product_name
字段并不依赖于order_id
(订单号),它仅依赖于product_id
(商品ID)。也就是说,product_name
是与product_id
紧密相关的,但不依赖于order_id
。
具体来说:
- 订单号 (
order_id
) 决定了订单的唯一性。 - 商品ID (
product_id
) 决定了商品的唯一性。 - 但商品的名字(
product_name
)只与商品ID (product_id
) 相关,而与订单号 (order_id
) 无关。
因此,product_name
并没有完全依赖于整个复合主键(order_id
和 product_id
),它只是部分依赖于 product_id
。这就违反了第二范式的要求。
为什么这违反了第二范式?
根据第二范式的要求,所有非主键字段必须完全依赖于复合主键,而不能只依赖于主键的一部分。在我们的设计中,product_name
只依赖于 product_id
,而不是完整的复合主键 (order_id, product_id)
。因此,product_name
存在部分依赖,违反了第二范式。
符合第二范式的设计:
为了使表符合第二范式,我们需要消除部分依赖。具体来说,我们可以将 product_name
字段移到一个单独的表中,专门存储商品信息,这样 product_name
就会只依赖于 product_id
,而不是依赖于复合主键 (order_id, product_id)
。
新的设计方案:
- 订单表 (
orders
):存储订单信息。
2.商品表 (products
):存储商品信息,每个商品有一个 product_id
和 product_name
。
3.订单明细表 (order_items
):存储每个订单的商品信息。主键依然是 (order_id, product_id)
,但是商品信息不再在此表中存储,而是通过 product_id
关联到 products
表。
数据插入示例:
插入订单数据:
插入商品数据:
插入订单明细数据:
解释:
product_name
现在存储在products
表中,且只依赖于product_id
。它不再依赖于复合主键(order_id, product_id)
。order_items
表只存储每个订单的商品和数量,不再包含商品名称,商品名称通过product_id
关联到products
表中。
通过这种设计:
product_name
不再存在部分依赖,它完全依赖于product_id
。order_items
表现在只存储与订单相关的信息,完全符合第二范式。
不满⾜第⼆范式时可能出现的问题
1. 数据冗余
学⽣的姓名、年龄、性别和课程的学分在每⾏记录中重复出现,造成了⼤量的数据冗余。
2. 更新异常
如果要调整MySQL的学分,那么就需要更新表中所有关于MySQL的记录,⼀旦执⾏中断导致某些 记录更新成功,某些数据更新失败,就会造成表中同⼀⻔课程出现不同学分的情况,出现数据不⼀致 问题。
3. 插⼊异常
⽬前这样的设计,成绩与每⼀⻔课和学⽣都有对应关系,也就是说只有学⽣参加选修课程考试取 得了成绩才能⽣成⼀条记录。当有⼀⻔新课还没有学⽣参加考试取得成绩之前,那么这⻔新课在数据 库中是不存在的,因为成绩为空时记录没有意义。
4. 删除异常
把毕业学⽣的考试数据全都删除,此时课程和学分的信息也会被删除掉,有可能导致⼀段时间 内,数据库⾥没有某⻔课程和学分的信息。
5.解决方案:
将依赖于主键一部分的字段(如 product_name
)移动到另一个独立的表中,确保每个非主键字段都完全依赖于整个复合主键。
第三范式(3NF):避免不必要的间接依赖
什么是第三范式?
第三范式要求数据表符合第二范式,并且消除传递依赖。传递依赖是指:如果字段A依赖于字段B,字段B又依赖于字段C,那么字段A间接依赖于字段C。为了避免这种情况,我们需要确保每个字段只直接依赖于主键。
第三范式(3NF)的要求:
- 表必须符合第二范式(2NF),即消除部分依赖。
- 消除传递依赖:如果一个非主键字段依赖于另一个非主键字段,那么这就是传递依赖。第三范式要求非主键字段只能直接依赖于主键,而不能间接依赖于主键。
什么是传递依赖?
假设有一个表,表中的字段依赖于主键的某些其他字段,而这些字段并不直接依赖于主键本身,而是通过其他非主键字段间接依赖。这种依赖称为传递依赖。
不符合第三范式的设计示例:
假设我们设计了一个员工表(employees
),包含 部门编号
和 部门名称
两个字段。
插入数据:
为什么这个设计违反了第三范式?
- 传递依赖的存在:
employee_id
是主键,它唯一标识每一行。department_id
依赖于employee_id
,而department_name
依赖于department_id
。因此,department_name
通过department_id
间接依赖于主键employee_id
。- 问题:
department_name
不应该依赖于department_id
,而是应该依赖于department_id
的表。我们应该消除这种间接依赖,确保所有的非主键字段只直接依赖于主键。
这种情况下,department_name
是通过 department_id
间接依赖于主键 employee_id
,这就是传递依赖。根据第三范式,非主键字段(department_name
)应当直接依赖于主键(employee_id
),而不是依赖于其他非主键字段。
符合第三范式的设计:
为了消除传递依赖,我们可以将 department_name
字段移动到一个独立的表中,专门存储部门信息。这样,department_name
就会直接依赖于 department_id
,而不会通过 employee_id
间接依赖。
新的设计方案:
- 员工表 (
employees
):只存储与员工相关的信息,去掉department_name
字段。
2.部门表 (departments
):存储部门信息,每个部门有 department_id
和 department_name
。
数据插入示例:
插入部门数据:
插入员工数据:
解释:
- 在
employees
表中,department_id
作为外键引用了departments
表中的department_id
,不再存储department_name
。 departments
表专门存储了部门信息,department_name
直接依赖于department_id
,而department_id
在employees
表中作为外键,依赖于employee_id
。
通过这种方式,department_name
不再依赖于 employee_id
,而是直接依赖于 department_id
,从而消除了传递依赖,符合第三范式。
总结:
- 传递依赖:在原设计中,
department_name
通过department_id
间接依赖于主键employee_id
,这就是传递依赖。 - 符合第三范式的设计:将
department_name
移到独立的部门表中,department_name
直接依赖于department_id
,而employee_id
只与员工信息相关,消除了传递依赖,符合第三范式。
通过这样的调整,表结构更加简洁,避免了数据冗余和更新异常,提高了数据库的一致性和完整性。
3. 设计过程
需求分析阶段
在开始设计数据表之前,首先需要进行详细的需求分析,这一阶段的关键是了解应用的业务需求和系统的功能。通过与客户、开发团队、产品经理等的沟通,收集以下信息:
- 业务流程:了解业务的流程和操作步骤,确定哪些数据需要存储。
- 数据类型:识别哪些数据需要存储,并明确所有数据的属性。
- 数据关系:明确不同数据之间的关系,是否需要多表关联,主键、外键如何设计。
- 查询需求:确定系统将如何查询、筛选和统计数据,这有助于升级优化索引和查询性能。
- 数据一致性:了解数据的完整性要求,确保数据的准确性和一致性。
2.定义实体和属性
在需求分析之后,接下来就是**定义实体(表)和属性(字段)**的阶段。这一步的目标是确定存储哪些数据及其具体的结构。你可以通过以下步骤完成这个阶段:
实体被识别
实体是需要存储在数据库中的业务对象。例如,在一个电商系统中,常见的实体可能包括:
用户
(用户)订单
(命令)商品
(产品)支付
(支付)
属性定义
每个实体都有自己的属性,这些属性决定了表中的列。比如,用户
实体可能有以下属性:
- 用户ID(
user_id
) - 用户名(
username
) - 密码(
password
) - 电子邮箱(
email
) - 注册时间(
registration_date
)
3.设计结构
根据后续实体和属性的定义,开始设计数据库表结构。包括以下几个关键点:
确定主键(Primary Key)
每个表格必须有一个主键,主键用于唯一标识一行数据,保证数据的唯一性。主键的选择原则:
- 唯一性:主键值必须唯一,不能重复。
- 不可为空:主键字段不能为空(NULL)。
- 稳定性:主键值应稳定,易于改变。
例如,用户
表的主键可以是user_id
。
选择项
为每个存储字段的数据选择合适的数据类型。例如:
- 对于字符串数据使用
VARCHAR
或TEXT
类型。 - 对于整数数据使用
INT
或BIGINT
类型。 - 对于时间日期数据使用
DATETIME
或TIMESTAMP
类型。
定义约束
- 唯一约束(
UNIQUE
):确保字段中的值是唯一的,例如用户名和电子邮件地址。 - 外键约束(
FOREIGN KEY
):定义表与表之间的关联关系,例如订单表
中的user_id
外键,指向用户表
的user_id
。 - 非空约束(
NOT NULL
):确保字段不能为空。 - 默认值(
DEFAULT
):字段没有值时使用的默认值。
设计索引(Index)
索引是为了加速查询而创建的数据结构。可以在用于经常查询条件的字段(如user_id
、order_date
)上创建索引,以提高查询性能。注意:
- 索引可以加速查询,但同时也会影响插入、更新操作的性能。
- 不要在每个字段上都创建索引,应该根据实际的查询需求来优化索引。
4. 实体-关系图
实体-关系图(Entity-RelationshipDiagram)简称E-R图,也称作实体联系模型、实体关系模型,是 ⼀种⽤于描述数据模型的概念图,主要⽤于数据库设计阶段。
E-R图的基本组成 E-R图包含了以下三种基本成分:
• 实体:即数据对象,⽤矩形框表⽰,⽐如⽤⼾、学⽣、班级等。
• 属性:实体的特性,⽤椭圆形或圆⻆矩形表⽰,如学⽣的姓名、年龄等。
• 关系:实体之间的联系,⽤菱形框表⽰,并标明关系的类型,并⽤直线将相关实体与关系连接起 来。
关系的类型:
1. ⼀对⼀关系(1:1)
• ⼀个⽤⼾实体包含的属性有:⽤⼾昵称,真实姓名,⼿机号,邮箱地址,性别,学校
• ⼀个账⼾实体包含的属性有:登录⽤⼾名,密码
• ⽤⼾实体与账⼾实体是⼀对⼀的关系,⽤E-R图表⽰如下:
2 ⼀对多关系(1:N)
• ⼀个学⽣实体包含的属性有:真实姓名,学号,年龄,性别,⼊学时间
• ⼀个班级实体包含的属性有:班级名,学⽣⼈数
• ⼀个班级中有多个学⽣,所以班级实体与学⽣实体是⼀对多的关系,反过来说学⽣实体与班级实体 是多对⼀着么,⽤E-R图表⽰如下:
3 多对多关系(M:N)
• ⼀个学⽣实体包含的属性有:真实姓名,学号,年龄,性别,⼊学时间
• ⼀个课程实体包含的属性有:课程名
• ⼀个学⽣可以选修改多⻔课程,⼀⻔课程也可以被多名学⽣选修改,所以学⽣与课程之间是多对多 关系,⽤E-R图表⽰如下:
• 对于多对多关系,可以使⽤中间表进⾏记录,⽐如⼀个学⽣参加了某⼀⻔课程的考试得到了相应的 成绩,⽤E-R图表⽰如下:
5.数据规范化
数据规范化是指通过剔除数据表,保证数据表的结构简洁、稳定性。规范化过程分为不同的范式,通常包括以下几个阶段:
- 第一范式(1NF):确保表中的每一列都是原子性的,即每一列只能包含一个值。
- 第二范式(2NF):确保每个非主键字段完全依赖于主键,消除部分依赖。
- 第三范式(3NF):消除供货依赖,确保每个非主键字段只直接依赖于主键。
6.性能。
在完成基本的表结构设计后,考虑到数据库的查询性能和扩展性,进行一些优化:
索引优化
- 对常用查询条件字段(如
user_id
、order_id
)建立索引。 - 对涉及范围查询(如时间区间查询)的字段建立合适的索引。
- 避免对小表创建过多的索引,因为它们会增加数据库的写入操作成本。
分表
如果表的数据量非常大,可能需要进行分表(水平分割)或分区(垂直分割)。例如,可以将订单表按日期分区,或者将用户表按地区分割表。
7.安全性和备份
- 数据安全性:确保对敏感数据(如密码)进行加密存储。可以使用加密算法存储密码,避免明文存储。
- 数据备份:设计数据库策略备份,定期备份数据库以防止数据丢失。
8.实现和测试
最后,将设计好的数据表转化为SQL语句,执行数据库创建操作。在实现过程中,确保:
- 测试数据的权限:确保表设计不违反数据约束、数据依赖等规则。
- 查询性能测试:测试常用的查询是否快速,特别是对于大数据量的查询。
总结:
在数据库设计的历程中,我们最大的架构师,构建了一座数据的大厦!从需求分析到表格结构设计,每一步都充满了挑战和创意。通过仔细梳理业务需求,我们为数据定义了语音的“身份”,让每个表都成为数据的家,确保他们井然有序且不显现。
我们不仅要保证数据的一致性,还要让它们始高效性,让查询速度
而为了应对未来的增长,我们更通过索引优化和**分区设计分区设计,让数据库
设计好一个数据库表,意味着你为数据架建立了一个坚固又高效的桥梁。通过表与表之间紧密的关系,每一条数据都能顺利流动,应用系统也能流畅运行。最后,我们还要时刻关注安全性和备份
总的来说,数据库设计不仅仅是技术工作,它是一门思维的艺术!每一个设计决策都决定了系统的未来,它是数据和业务需求聚合的完美融合。希望数据库设计的旅程,能够让你愉快地掌握如何设计出、安全、易于维护的高效数据库系统