数据库通用面试(三)
原创 Suren 婺青年 2023-08-14 11:14 发表于上海 收录于合集 #面试14个 #数据库5个 #sql7个数据库范式详解
数据库范式:让你的数据库更规范、更高效
你知道什么是数据库范式吗?为什么要用数据库范式呢?数据库范式又有哪些呢?如果你对这些问题感到困惑,那么这篇文章就是为你准备的。在这篇文章中,我将用简单易懂的语言,用丰富的例子和图表,用有趣的语气和风格,来向你介绍数据库范式的概念、目的和内容。希望你能从中学到一些有用的知识,也能享受阅读的乐趣。
什么是数据库范式?
首先,我们要明白什么是数据库。数据库(Database)是按照数据结构来组织、存储和管理数据的仓库。我们可以把数据库想象成一个巨大的图书馆,里面存放了各种各样的书籍,每本书都有自己的编号、分类、作者、出版社等信息。我们可以通过这些信息来查找、借阅、归还书籍。
那么,什么是数据库范式呢?数据库范式(Database Normalization)是一种设计关系型数据库的标准,目的是为了减少数据冗余(重复),避免数据异常(错误),提高数据的完整性(正确)和一致性(统一)。我们可以把数据库范式想象成一个图书馆的规则,它告诉我们如何把书籍分门别类地放在不同的书架上,如何给每本书贴上合适的标签,如何保证每本书都有唯一的编号,如何避免同一本书被重复购买或丢失等。
为什么要用数据库范式?
那么,为什么要用数据库范式呢?我们不用数据库范式会有什么坏处呢?让我们来看一个例子。
假设我们要建立一个学生选课的数据库,里面有一个表格叫做student_course,记录了每个学生选择了哪些课程。表格中有三个字段:student_id(学号)、course_name(课程名)和teacher_name(教师名)。表格可能长这样:
student_id | course_name | teacher_name |
---|---|---|
1001 | 数学 | 李老师 |
1001 | 英语 | 王老师 |
1002 | 数学 | 李老师 |
1002 | 英语 | 王老师 |
这个表格看起来很简单,但是它有很多问题。比如:
-
数据冗余:同样的信息被重复存储了多次,比如李老师教数学这个信息出现了两次。这样会浪费存储空间,也会增加数据维护的难度。
-
数据异常:如果数据发生了变化,比如李老师不再教数学了,那么就需要修改多条记录中的教师名信息,这既麻烦又容易出错。如果忘记修改某条记录,就会导致数据不一致,比如有些记录显示李老师教数学,有些记录显示李老师不教数学。
-
数据完整性:如果某个字段没有值,比如某个学生没有选课,那么就会出现空值。空值会影响数据的完整性,也会给数据查询和分析带来困难。
为了解决这些问题,我们就需要用到数据库范式。数据库范式可以帮助我们把一个大的表格分解成多个小的表格,每个表格只包含相关的信息,每个字段都有明确的含义,每条记录都是唯一的。这样,我们就可以减少数据冗余,避免数据异常,提高数据完整性和一致性。
数据库范式有哪些?
数据库范式有六种,从低到高分别是第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF)。一般来说,数据库设计只需要满足第三范式就可以了,但有时候也需要考虑更高的范式。每一种范式都有其含义和要求,需要根据具体的情况进行合理的选择和应用。下面我们来依次介绍这六种范式。
第一范式(1NF)
第一范式是最基本的范式,要求数据库表中的每个字段都是不可分割的原子值。也就是说,每个字段都只能存储一个值,不能存储多个值或者复杂的结构。例如,如果一个表有一个字段叫做hobby(爱好),那么这个字段不能存储多个爱好,比如“游泳、唱歌、跳舞”,而只能存储一个爱好,比如“游泳”。如果一个学生有多个爱好,那么就需要用多条记录来表示,每条记录只存储一个爱好。
下面是一个不符合第一范式的表:
student_id | name | hobby |
---|---|---|
1001 | 张三 | 游泳、唱歌、跳舞 |
1002 | 李四 | 看书、画画 |
下面是一个符合第一范式的表:
student_id | name | hobby |
---|---|---|
1001 | 张三 | 游泳 |
1001 | 张三 | 唱歌 |
1001 | 张三 | 跳舞 |
1002 | 李四 |
第二范式(2NF)
第二范式是在第一范式的基础上进一步提高的标准,要求数据库表中不存在非主属性对候选键的部分函数依赖。这句话听起来很绕口,我们来解释一下其中的概念:
-
主属性:指那些包含在任何一个候选键中的属性。
-
非主属性:指那些不包含在任何一个候选键中的属性。
-
候选键:指能够唯一标识一条记录的属性或属性组。一个表中可能有多个候选键,但只能选一个作为主键。
-
主键:指被选作为主要标识符的候选键。
-
函数依赖:指一个属性或属性组可以决定另一个属性或属性组的值。比如学号可以决定姓名,那么就说姓名函数依赖于学号。
-
部分函数依赖:指非主属性函数依赖于候选键的一部分。比如课程名函数依赖于(学号、课程名)的一部分学号。
简单地说,第二范式就是要求一个表中只描述一种事物,不能把多种事物的信息放在同一个表中,而要把它们分开成多个表,每个表都有自己的主键。这样可以避免数据冗余和数据异常。
下面是一个不符合第二范式的表:
student_id | course_name | teacher_name | name |
---|---|---|---|
1001 | 数学 | 李老师 | 张三 |
1001 | 英语 | 王老师 | 张三 |
1002 | 数学 | 李老师 | 李四 |
1002 | 英语 | 王老师 | 李四 |
这个表中,(student_id,course_name)是候选键,也是主键。name是非主属性,它函数依赖于student_id,也就是说,只要知道学号,就能知道姓名。这就是部分函数依赖。这样会导致数据冗余,比如张三的名字出现了两次。如果张三改名了,那么就需要修改两条记录,否则就会出现数据不一致。
下面是一个符合第二范式的表:
student_id | course_name | teacher_name |
---|---|---|
1001 | 数学 | 李老师 |
1001 | 英语 | 王老师 |
1002 | 数学 | 李老师 |
1002 |
student_id | name |
---|---|
1001 | 张三 |
1002 | 李四 |
我们把原来的表分成了两个表,一个表存储学生和课程的信息,另一个表存储学生和姓名的信息。两个表都用student_id作为主键,可以通过它来关联两个表。这样就消除了数据冗余和数据异常。
第三范式(3NF)
第三范式是在第二范式的基础上进一步提高的标准,要求数据库表中不存在非主属性对候选键的传递函数依赖。这句话同样很绕口,我们来解释一下其中的概念:
-
传递函数依赖:指如果 A 函数依赖于 B,B 函数依赖于 C,那么 A 就传递函数依赖于 C。比如姓名函数依赖于学号,学号函数依赖于身份证号,那么姓名就传递函数依赖于身份证号。
简单地说,第三范式就是要求一个表中的每个字段都直接依赖于主键,而不是间接依赖于主键。也就是说,一个表中不能有其他的候选键或者非主属性之间的关系,而只能有主键和非主属性之间的关系。这样可以进一步减少数据冗余和数据异常。
下面是一个不符合第三范式的表:
student_id | name | id_number |
---|---|---|
1001 | 张三 | 110101200101010001 |
1002 | 李四 | 110101200101010002 |
这个表中,student_id是候选键,也是主键。name和id_number都是非主属性。name函数依赖于student_id,id_number也函数依赖于student_id。但是id_number还函数依赖于name,因为每个人的身份证号都是唯一的。这就是传递函数依赖。这样会导致数据冗余,比如张三的身份证号出现了两次。如果张三换了身份证号,那么就需要修改两条记录,否则就会出现数据不一致。
下面是一个符合第三范式的表:
student_id | name |
---|---|
1001 | 张三 |
1002 | 李四 |
name | id_number |
---|---|
张三 | 110101200101010001 |
李四 | 110101200101010002 |
我们把原来的表分成了两个表,一个表存储学生和姓名的信息,另一个表存储姓名和身份证号的信息。两个表都用name作为主键,可以通过它来关联两个表。这样就消除了数据冗余和数据异常。
巴斯-科德范式(BCNF)
巴斯-科德范式是在第三范式的基础上进一步提高的标准,要求数据库表中不存在主属性对候选键的部分函数依赖或传递函数依赖。这句话比前面的更绕口,我们来解释一下其中的概念:
-
主属性对候选键的部分函数依赖:指主属性函数依赖于候选键的一部分。比如教师名函数依赖于(课程名、教室号)的一部分课程名。
-
主属性对候选键的传递函数依赖:指主属性传递函数依赖于候选键。比如教师名函数依赖于课程名,课程名函数依赖于课程编号,那么教师名就传递函数依赖于课程编号。
简单地说,巴斯-科德范式就是要求一个表中的每个字段都只依赖于候选键,而不是候选键的一部分或者其他字段。也就是说,一个表中不能有多个候选键之间或者候选键和主属性之间的关系,而只能有候选键和非主属性之间的关系。这样可以进一步保证数据的完整性和一致性。
下面是一个不符合巴斯-科德范式的表:
course_name | classroom_number | teacher_name |
---|---|---|
数学 | 101 | 李老师 |
英语 | 102 | 王老师 |
这个表中,(course_name,classroom_number)是候选键,teacher_name是主属性。teacher_name函数依赖于course_name,也就是说,只要知道课程名,就能知道教师名。这就是主属性对候选键的部分函数依赖。这样会导致数据不完整,比如如果有两个教师都教数学,那么就无法区分他们。
下面是一个符合巴斯-科德范式的表:
course_name | teacher_name |
---|---|
数学 | 李老师 |
英语 | 王老师 |
course_name | classroom_number |
---|---|
数学 | 101 |
英语 | 102 |
我们把原来的表分成了两个表,一个表存储课程和教师的信息,另一个表存储课程和教室的信息。两个表都用course_name作为主键,可以通过它来关联两个表。这样就保证了数据的完整性和一致性。
第四范式(4NF)
第四范式是在巴斯-科德范式的基础上进一步提高的标准,要求数据库表中消除非平凡的多值依赖。多值依赖是指一个属性或属性组可以决定另外两个或多个属性或属性组,而这些属性或属性组之间相互独立。例如,如果一个表有三个属性:学号、课程名和教师名,那么学号可以决定课程名和教师名,但课程名和教师名之间没有关系,也就是说,一个学生可以选不同的课程,一个课程可以有不同的教师。这就是多值依赖。非平凡的多值依赖是指多值依赖中的属性或属性组不包含在候选键中。例如,学号对课程名和教师名的多值依赖就是非平凡的,因为课程名和教师名都不包含在候选键中。
简单地说,第四范式就是要求一个表中不能有多对多的关系,而要把它们分开成多个一对多的关系。也就是说,一个表中不能有两个或多个属性或属性组都依赖于另一个属性或属性组,而要把它们分开成多个表,每个表只有一个属性或属性组依赖于另一个属性或属性组。这样可以进一步减少数据冗余和数据异常。
下面是一个不符合第四范式的表:
student_id | course_name | teacher_name |
---|---|---|
1001 | 数学 | 李老师 |
1001 | 英语 | 王老师 |
1002 | 数学 | 李老师 |
这个表中,student_id是候选键,也是主键。course_name和teacher_name都是非主属性。student_id对course_name和teacher_name都有非平凡的多值依赖,也就是说,一个学生可以选多门课程,一个课程可以有多个教师。这就是多对多的关系。这样会导致数据冗余,比如李老师教数学这个信息出现了两次。如果李老师不再教数学了,那么就需要修改两条记录,否则就会出现数据不一致。
下面是一个符合第四范式的表:
student_id | course_name |
---|---|
1001 | 数学 |
1001 | 英语 |
course_name | teacher_name |
---|---|
数学 | 李老师 |
英语 | 王老师 |
我们把原来的表分成了两个表,一个表存储学生和课程的信息,另一个表存储课程和教师的信息。两个表都用course_name作为主键,可以通过它来关联两个表。这样就消除了数据冗余和数据异常。
第五范式(5NF)
第五范式是在第四范式的基础上进一步提高的标准,要求数据库表中消除非平凡的连接依赖。连接依赖是指一个表可以由两个或多个其他的表通过连接操作(join)得到。例如,如果有三个表:A、B、C,那么如果A join B join C等于原来的表,那么就说原来的表有连接依赖。非平凡的连接依赖是指连接依赖中的表不包含候选键。例如,如果原来的表有三个字段:a、b、c,而A、B、C分别只有一个字段:a、b、c,那么原来的表就有非平凡的连接依赖。
简单地说,第五范式就是要求一个表中不能有多余的信息,而要把它们分开成最小的单元。也就是说,一个表中不能有两个或多个字段之间没有直接关系,而只是通过另一个字段间接关联,而要把它们分开成多个表,每个表只包含有直接关系的字段。这样可以进一步保证数据的独立性和灵活性。
下面是一个不符合第五范式的表:
a | b | c |
---|---|---|
1 | 2 | 3 |
1 | 4 | 5 |
这个表中,(a,b,c)是候选键,也是主键。这个表有非平凡的连接依赖,因为它可以由三个表A、B、C通过连接操作得到,而A、B、C分别只有一个字段:a、b、c。这样会导致数据多余,比如1和2之间没有直接关系,而只是通过3和4间接关联。
下面是一个符合第五范式的表:
a | b |
---|---|
1 | 2 |
1 | 4 |
a | c |
---|---|
1 | 3 |
1 | 5 |
b | c |
---|---|
2 | 3 |
4 | 5 |
我们把原来的表分成了三个表,一个表存储a和b的信息,一个表存储a和c的信息,一个表存储b和c的信息。三个表都用两个字段作为主键,可以通过它们来连接得到原来的表。这样就消除了数据多余。
总结
以上就是我对数据库范式的概念、目的和内容的介绍。我希望你能通过这篇文章,对数据库范式有了一个初步的认识和理解。当然,数据库范式还有很多细节和应用,需要你进一步学习和探索。在实际的数据库设计中,也不一定要严格遵守所有的范式,而要根据具体的需求和场景进行权衡和选择。数据库范式只是一种指导原则,而不是一种铁律。最终的目标是让你的数据库更规范、更高效。
Suren 收录于合集 #面试 14个 上一篇数据库通用面试(二)下一篇MySQL面试题(基础篇一) 阅读 13 婺青年 关注后可发消息 标签:候选,范式,name,数据库,表中,设计规范,格式,属性 From: https://www.cnblogs.com/cherishthepresent/p/17648016.html