为什么要遵循范式?
通过应用范式化设计,可以提高数据库的结构清晰性、数据一致性和操作效率。然而,范式化也可能导致查询复杂性增加和数据冗余的增加。因此,在实际应用中,需要根据具体需求和性能要求进行适当的范式化设计。
什么是数据库三大范式?
数据库的范式,是一种规范化的设计方法,主要用于优化数据库的结构和操作效率。范式可以分为多个级别,其中最常用的是三种范式,即第一范式(1NF)、第二范式(2NF)和第三范式(3NF)。
第一范式(1NF)要求数据库中的每个列必须是原子性的,即不可再分解。也就是说,每个列中的每个值都必须是唯一的,并且不可再细分为更小的数据。
第二范式(2NF)在满足第一范式的基础上,要求数据库中的每个非主键列必须完全依赖于主键列。换句话说,每个非主键列的值必须与主键关联,并且不能依赖其他非主键列。
第三范式(3NF)在满足第二范式的基础上,要求数据库中的每个非主键列都必须直接依赖于主键列,而不能间接依赖于其他非主键列。也就是说,任何非主键列的值都不能通过其他非主键列推导出来。
最佳实践
第一范式
如何将一个非规范化的表转换为满足第一范式(1NF)的形式
假设有如下非规范化的表,记录了某个公司的员工信息:
在上述表结构中,每个记录中有多个属性(姓名、职位、工资),这违反了第一范式的要求,因为每个列必须是原子性的。
为了满足第一范式,我们可以将上述表进行拆分,得到两个规范化的表:
表1:员工信息(Employee)
表2:职位信息(Position)
现在,每个表都满足了第一范式的要求,每个列都是原子性的,没有多个值。这样的数据结构更加规范和易于管理。
举一个反例,展示一个不符合第一范式(1NF)以及如何拆分正确使用第一范式
假设有如下表,记录了某个班级的学生信息:
在上述表结构中,一列中存储了多个学生的姓名,违反了第一范式的要求,因为每个列必须是原子性的。
为了满足第一范式,我们可以将上述表进行拆分,得到两个规范化的表:
表1:班级信息(Class)
表2:学生信息(Student)
表3:班级与学生关联信息(Class_Student)
通过拆分表并引入关联表,每个表都满足了第一范式的要求,每个列都是原子性的。这样的数据结构更加规范和易于管理。
第二范式
第二范式(2NF)要求一个表中的所有列都必须完全依赖于表的主键。也就是说,如果一个表有多个候选键,则每个非主键列必须完全依赖于每个候选键。
正例:
考虑一个订单系统。假设我们有如下订单表:
Order (order_id, customer_id, customer_name, product_id, product_name, quantity, price)
在这个表中,order_id 是主键,而其他列并不完全依赖于主键。例如,customer_name和product_name 列依赖于 customer_id 和 product_id 列。为了满足第二范式,我们可以将表拆分成多个表,如下所示:
Order (order_id, customer_id, product_id, quantity, price)
Customer (customer_id, customer_name)
Product (product_id, product_name)
现在每个表都符合第二范式的要求:主键和非主键列之间的关系被明确定义了。
反例:
假设有如下图书馆图书借阅记录的表:
Book_loans (Book_id, Title, Author, Borrower_id, Borrower_name, Checkout_date, Due_date)
在这个表中,Book_id 和 Borrower_id 是联合主键。但是 Title 和 Author 列并不完全依赖于 Book_id,而 Borrower_name 列也并不完全依赖于 Borrower_id。因此,这个表不符合第二范式的要求。
为了满足第二范式,我们可以将表拆分成多个表,如下所示:
Books (Book_id, Title, Author)
Borrowers (Borrower_id, Borrower_name)
Loans (Book_id, Borrower_id, Checkout_date, Due_date)
现在每个表都符合第二范式的要求:主键和非主键列之间的关系被明确定义了。
第三范式
第三范式要求一个表中的每一列必须都与主键直接相关,而不能依赖于其他非主键列。也就是说,一个表中不能存在传递依赖关系。
正例
考虑一个学校课程表。假设我们有如下表:
Course_schedule (Course_code, Course_name, Department, Instructor, Instructor_office)
在这个表中,Course_code 是主键。但是 Department 列依赖于 Course_code 和 Instructor 列,而 Instructor_office 列依赖于 Instructor 列。因此,这个表不符合第三范式的要求。
为了满足第三范式,我们可以将表拆分成多个表,如下所示:
Course (Course_code, Course_name, Instructor)
Department (Course_code, Instructor, Department)
Instructor_office (Instructor, Instructor_office)
现在每个表都符合第三范式的要求:每一列都与主键直接相关。
反例:
假设有如下订单系统。假设我们有如下订单表:
Order (order_id, customer_id, customer_name, customer_address, product_id, product_name, price, order_date)
在这个表中,order_id 是主键。但是 customer_name 和 customer_address 列依赖于 customer_id 列。因此,这个表不符合第三范式的要求。
为了满足第三范式,我们可以将表拆分成多个表,如下所示:
Order (order_id, customer_id, product_id, price, order_date)
Customer (customer_id, customer_name, customer_address)
现在每个表都符合第三范式的要求:每一列都与主键直接相关。