数据库设计三范式(NF)之第二范式极简说明

一句话核心思想

第二范式就是:一张表只干一件事。

更专业一点说:确保表中的每列都完全依赖于整个主键,而不是只依赖于主键的一部分。


用一个生动的例子来解释

想象一下,我们有一张 订单明细表 来记录超市的购物小票。

订单ID (主键) 商品ID (主键) 商品名称 单价 数量 顾客姓名 顾客电话
1001 A01 可口可乐 3.5 2 张三 13800138000
1001 B02 乐事薯片 8.0 1 张三 13800138000
1002 A01 可口可乐 3.5 5 李四 13900139000

这张表有什么问题?(它违反第二范式)

  1. 冗余重复:

    • 同一个订单ID(1001)出现了两次,顾客“张三”和他的电话也被重复存储了。

    • 商品“可口可乐”的单价(3.5)在多个订单中重复出现。如果可乐涨价了,我们需要修改表中所有包含了“A01”商品的行,非常容易出错和遗漏。

  2. 更新异常:

    • 如果张三换了手机号,我必须找到所有订单ID=1001的记录,一条一条地去修改顾客电话,而不能只修改一次。

  3. 插入异常:

    • 如果有一个新商品“C03-农夫山泉”刚刚入库,但还没有任何订单购买过它,我们就无法把这个商品的信息(名称、单价)插入到这张表里。因为缺少主键的另一部分(订单ID)。

  4. 删除异常:

    • 如果订单1001的“乐事薯片”这条记录被删除了,我们可能就彻底丢失了“张三”这个顾客的信息,因为他只有一个订单。

为什么会这样?

因为这张表不止干了一件事!它混杂了:

  • 订单的详细信息(哪个订单买了哪个商品,买了多少)

  • 商品自身的信息(商品叫什么,卖多少钱)

  • 顾客的信息(谁买的,联系方式)

它的主键是(订单ID, 商品ID),这是一个联合主键。

  • 数量 完全依赖于整个主键(我必须同时知道订单号和商品号,才能确定买了几件)。

  • 但是 顾客姓名 和 顾客电话 只依赖于订单ID。只要我知道订单号1001,我就知道顾客是张三,不需要知道商品ID是什么。

  • 同样,商品名称 和 单价 只依赖于商品ID。只要我知道商品ID是A01,我就知道它是可口可乐,价格3.5元,不需要知道是哪个订单买的。

这种“只依赖于部分主键”的列,就是违反第二范式的根源。


如何改造?(使其符合第二范式)

核心操作:拆表!让每张表只干一件事。

我们把上面那张大杂烩表拆成三张表:

1. 订单表 (专门管订单和顾客的关系)

订单ID (主键) 顾客姓名 顾客电话
1001 张三 13800138000
1002 李四 13900139000

2. 商品表 (专门管商品信息)

商品ID (主键) 商品名称 单价
A01 可口可乐 3.5
B02 乐事薯片 8.0
C03 农夫山泉 2.0 (新商品可以独立添加了!)

3. 订单明细表 (专门管订单买了什么商品)

订单ID (联合主键) 商品ID (联合主键) 数量
1001 A01 2
1001 B02 1
1002 A01 5

改造后的好处:

  • 数据冗余大大减少:顾客信息只存一次,商品信息只存一次。

  • 避免更新异常:张三换手机号,只需在订单表里修改一次。可乐涨价,只需在商品表里修改一次。

  • 避免插入异常:新商品“农夫山泉”可以直接添加到商品表,哪怕它还没被卖出过。

  • 避免删除异常:即使删除了订单1001的薯片记录,张三的信息依然安全地保存在订单表里。


总结:如何判断和满足第二范式?

  1. 第一步:确保你的表已经满足了第一范式(每个字段都是不可再分的原子值)。

  2. 第二步:看看你的表有没有联合主键(由多个字段组成的主键)。

  3. 第三步:检查表中的每一个字段,问自己:

    “这个字段是完全依赖于整个主键,还是只依赖于主键中的一部分?”

  4. 第四步:如果发现只依赖于部分主键的字段,就把它们拆出去,放到另一张表里,并为它们建立新的主键。

最终记住那个简单的比喻:一个好的数据库设计就像一个好的公司架构,每个部门(表)职责单一,专业高效,而不是一个部门什么都管,混乱不堪。

来源链接:https://www.cnblogs.com/zp123456/p/19049977

请登录后发表评论

    没有回复内容