mysql菜鸟教程
10.3 第二范式(2NF)
第一范式(1NF)解决了数据的原子性和重复组问题,但即使表满足了1NF,仍然可能存在数据冗余和更新异常。第二范式(2NF)的目标是进一步消除这些隐患,它要求表在满足1NF的基础上,消除非主属性对候选键的部分函数依赖。
为了理解2NF,我们需要先搞清楚几个关键概念:函数依赖、完全依赖、部分依赖、候选键和主属性/非主属性。
一、基础知识回顾
函数依赖
如果通过某一列(或列组合)的值可以唯一确定另一列的值,我们就说后者函数依赖于前者。用符号表示:如果知道X的值,就能知道Y的值,则称 X → Y(Y函数依赖于X)。
例如,在学生表中,学号 → 姓名(知道学号就能知道姓名),姓名不一定 → 学号(因为可能有重名)。
候选键与主键
候选键:能够唯一标识表中每一行的一个列或列组合。一个表可能有多个候选键。
主键:从候选键中选出的一个作为主要标识。
主属性:构成任何一个候选键的列。
非主属性:不包含在任何候选键中的列。
部分依赖与完全依赖
假设一个表的候选键由多个列组成(复合主键)。
完全依赖:如果一个非主属性必须依赖于整个候选键(所有列),而不能只依赖于其中一部分,则称为完全依赖。
部分依赖:如果一个非主属性只依赖于候选键的一部分列(而不是全部),则称为部分依赖。
第二范式的核心就是消除部分依赖。
二、一个不符合2NF的典型例子
让我们用一个订单明细表来说明。假设有一个 order_details 表,记录了订单中包含的商品信息。表结构如下:
分析:
候选键是 (order_id, product_id),因为一个订单中同一商品只会出现一次(可设计如此)。
主属性:order_id, product_id。
非主属性:product_name, quantity, price。
现在来看函数依赖:
(order_id, product_id) → quantity(因为确定了订单和商品,就确定了数量)——完全依赖。
(order_id, product_id) → price(价格依赖于订单和商品吗?实际上价格只依赖于商品本身,但与订单无关)——所以 price 只依赖于 product_id,属于部分依赖。
product_id → product_name(商品名称只由商品ID决定),也是部分依赖。
这个表存在部分依赖,因此不满足第二范式。
存在的问题
数据冗余:同一个商品(如手机)的名称和价格在多个订单中重复存储。
更新异常:如果手机价格调整,需要更新所有包含手机的订单记录,一旦漏改,数据就不一致。
插入异常:如果要新增一种商品(比如新的商品ID 104,名称为“平板”,价格 1999),但还没有任何订单包含它,就无法插入(因为主键需要 order_id,而 order_id 不能为 NULL)。
删除异常:如果删除某个订单的最后一条记录,可能同时删除了商品的信息(比如删除包含平板电脑的唯一订单,平板电脑的信息就从数据库中消失了)。
三、如何转换为2NF?
解决方法是将部分依赖的那些列拆分到新的表中,让每个非主属性都完全依赖于主键。
针对上面的例子,我们可以拆分成两张表:
商品表(products):
订单明细表(order_items):
现在:
order_items 表的主键仍是 (order_id, product_id),所有非主属性(只有 quantity)完全依赖于整个主键,没有部分依赖。
products 表的主键是 product_id,所有非主属性完全依赖于它。
这样,我们消除了部分依赖,使两个表都满足第二范式。
四、第二范式的完整定义
一个关系模式 R 属于第二范式(2NF),当且仅当它属于第一范式(1NF),并且每一个非主属性都完全函数依赖于 R 的任何一个候选键。
换句话说,2NF不允许存在非主属性对候选键的部分依赖。如果表的候选键是单列,那么它自动满足2NF(因为没有部分依赖可言)。所以2NF主要关注那些具有复合主键的表。
五、练习与思考
练习1
考虑一个 student_courses 表,记录学生选课情况:
问:这个表是否符合2NF?如果不符合,请指出部分依赖并分解。
分析:
候选键:(student_id, course_id)
非主属性:student_name, course_name, grade
函数依赖:
student_id → student_name(部分依赖)
course_id → course_name(部分依赖)
(student_id, course_id) → grade(完全依赖)存在部分依赖,不符合2NF。
分解:
学生表 students:student_id, student_name
课程表 courses:course_id, course_name
选课表 enrollments:student_id, course_id, grade
练习2
为什么说如果一张表的主键是单列,它就一定满足2NF?
答案:因为单列主键不存在部分依赖。部分依赖的定义是依赖于主键的一部分,而单列主键只有一列,所以任何非主属性要么完全依赖于该主键,要么完全不依赖(但非主属性必须依赖于主键?不一定,但如果是非主属性且不依赖主键,那它应该是主属性的一部分,这种情况可能属于更高范式问题)。但在2NF的上下文中,单列主键的表自动满足2NF。
六、总结
第二范式(2NF) 在1NF的基础上,要求消除非主属性对候选键的部分依赖。
它主要解决具有复合主键的表中的冗余和异常问题。
通过将表拆分成多个表,使每个非主属性都完全依赖于主键,可以达到2NF。
满足2NF后,数据冗余减少,更新、插入、删除异常得到缓解,但可能还存在其他依赖问题(如传递依赖),这需要第三范式(3NF)来解决。

发表评论
所有评论