mysql菜鸟教程

专栏导航

10.3 第二范式(2NF)

       第一范式(1NF)解决了数据的原子性和重复组问题,但即使表满足了1NF,仍然可能存在数据冗余和更新异常。第二范式(2NF)的目标是进一步消除这些隐患,它要求表在满足1NF的基础上,消除非主属性对候选键的部分函数依赖

为了理解2NF,我们需要先搞清楚几个关键概念:函数依赖完全依赖部分依赖候选键主属性/非主属性

一、基础知识回顾

函数依赖

如果通过某一列(或列组合)的值可以唯一确定另一列的值,我们就说后者函数依赖于前者。用符号表示:如果知道X的值,就能知道Y的值,则称 X → Y(Y函数依赖于X)。

例如,在学生表中,学号 → 姓名(知道学号就能知道姓名),姓名不一定 → 学号(因为可能有重名)。

候选键与主键

  • 候选键:能够唯一标识表中每一行的一个列或列组合。一个表可能有多个候选键。

  • 主键:从候选键中选出的一个作为主要标识。

  • 主属性:构成任何一个候选键的列。

  • 非主属性:不包含在任何候选键中的列。

部分依赖与完全依赖

假设一个表的候选键由多个列组成(复合主键)。

  • 完全依赖:如果一个非主属性必须依赖于整个候选键(所有列),而不能只依赖于其中一部分,则称为完全依赖。

  • 部分依赖:如果一个非主属性只依赖于候选键的一部分列(而不是全部),则称为部分依赖。

第二范式的核心就是消除部分依赖

二、一个不符合2NF的典型例子

让我们用一个订单明细表来说明。假设有一个 order_details 表,记录了订单中包含的商品信息。表结构如下:

order_id

product_id

product_name

quantity

price

1

101

手机

2

2999

1

102

充电器

1

99

2

101

手机

1

2999

2

103

耳机

3

199

分析

  • 候选键是 (order_id, product_id),因为一个订单中同一商品只会出现一次(可设计如此)。

  • 主属性:order_idproduct_id

  • 非主属性:product_namequantityprice

现在来看函数依赖:

  • (order_id, product_id) → quantity(因为确定了订单和商品,就确定了数量)——完全依赖。

  • (order_id, product_id) → price(价格依赖于订单和商品吗?实际上价格只依赖于商品本身,但与订单无关)——所以 price 只依赖于 product_id,属于部分依赖

  • product_id → product_name(商品名称只由商品ID决定),也是部分依赖。

这个表存在部分依赖,因此不满足第二范式

存在的问题

  1. 数据冗余:同一个商品(如手机)的名称和价格在多个订单中重复存储。

  2. 更新异常:如果手机价格调整,需要更新所有包含手机的订单记录,一旦漏改,数据就不一致。

  3. 插入异常:如果要新增一种商品(比如新的商品ID 104,名称为“平板”,价格 1999),但还没有任何订单包含它,就无法插入(因为主键需要 order_id,而 order_id 不能为 NULL)。

  4. 删除异常:如果删除某个订单的最后一条记录,可能同时删除了商品的信息(比如删除包含平板电脑的唯一订单,平板电脑的信息就从数据库中消失了)。

三、如何转换为2NF?

解决方法是将部分依赖的那些列拆分到新的表中,让每个非主属性都完全依赖于主键。

针对上面的例子,我们可以拆分成两张表:

商品表(products)

product_id

product_name

price

101

手机

2999

102

充电器

99

103

耳机

199

订单明细表(order_items)

order_id

product_id

quantity

1

101

2

1

102

1

2

101

1

2

103

3

现在:

  • order_items 表的主键仍是 (order_id, product_id),所有非主属性(只有 quantity)完全依赖于整个主键,没有部分依赖。

  • products 表的主键是 product_id,所有非主属性完全依赖于它。

这样,我们消除了部分依赖,使两个表都满足第二范式。

四、第二范式的完整定义

一个关系模式 R 属于第二范式(2NF),当且仅当它属于第一范式(1NF),并且每一个非主属性都完全函数依赖于 R 的任何一个候选键。

换句话说,2NF不允许存在非主属性对候选键的部分依赖。如果表的候选键是单列,那么它自动满足2NF(因为没有部分依赖可言)。所以2NF主要关注那些具有复合主键的表。

五、练习与思考

练习1

考虑一个 student_courses 表,记录学生选课情况:

student_id

course_id

student_name

course_name

grade

1

C01

张三

数据库

85

1

C02

张三

网络

90

2

C01

李四

数据库

78

问:这个表是否符合2NF?如果不符合,请指出部分依赖并分解。

分析

  • 候选键:(student_id, course_id)

  • 非主属性:student_namecourse_namegrade

  • 函数依赖:

    • student_id → student_name(部分依赖)

    • course_id → course_name(部分依赖)

    • (student_id, course_id) → grade(完全依赖)存在部分依赖,不符合2NF。

分解

  • 学生表 studentsstudent_idstudent_name

  • 课程表 coursescourse_idcourse_name

  • 选课表 enrollmentsstudent_idcourse_idgrade

练习2

为什么说如果一张表的主键是单列,它就一定满足2NF?

答案:因为单列主键不存在部分依赖。部分依赖的定义是依赖于主键的一部分,而单列主键只有一列,所以任何非主属性要么完全依赖于该主键,要么完全不依赖(但非主属性必须依赖于主键?不一定,但如果是非主属性且不依赖主键,那它应该是主属性的一部分,这种情况可能属于更高范式问题)。但在2NF的上下文中,单列主键的表自动满足2NF。

六、总结

  • 第二范式(2NF) 在1NF的基础上,要求消除非主属性对候选键的部分依赖。

  • 它主要解决具有复合主键的表中的冗余和异常问题。

  • 通过将表拆分成多个表,使每个非主属性都完全依赖于主键,可以达到2NF。

  • 满足2NF后,数据冗余减少,更新、插入、删除异常得到缓解,但可能还存在其他依赖问题(如传递依赖),这需要第三范式(3NF)来解决。


发表评论

昵称:
联系方式:
评论内容:

所有评论

关于我 备案号:蜀ICP备2023042032号-1