文章目录
- 问题
- 函数依赖
- 函数依赖的作用
- 函数依赖的性质
- 重新理解主键
- 总结
- 系列文章
问题
考虑一个在线商店数据库,其中包含以下表:
【订单表】
Order_Num(订单号) | Product_ID(产品ID) | Count(数量) | Price(单价) |
---|---|---|---|
1 | 10 | 2 | 100 |
2 | 10 | 1 | 100 |
3 | 20 | 3 | 200 |
你觉得这张表是否存在问题?存在哪些问题?
- 数据不一致。同一产品可能有多个不同的单价,导致计算错误。例如,因写入失误,订单1和订单2中产品10的单价不一致,导致后续统计结果出错。
- 数据冗余。数据冗余除了可能导致数据不一致的问题之外(问题1),还存在空间浪费的问题。例如,每条包含产品10的订单,都会存储单价信息。
应该怎么解决这两个问题呢?
- 首先要消除冗余数据,让每个产品的单价只存储一份。假设存在另一张产品单价表,保存每个产品的单价,如下所示:
【单价表】
Product_ID (产品ID,主键) | Price(单价) |
---|---|
10 | 100 |
20 | 200 |
- 然后剔除订单表中的单价列,并将产品ID作为外键,如下所示:
【优化后的订单表】
Order_Num(订单号,主键) | Product_ID(产品ID,外键) | Count(数量) |
---|---|---|
1 | 10 | 2 |
2 | 10 | 1 |
3 | 20 | 3 |
- 通过Product_ID外键关联订单表和单价表,可以解决数据冗余和数据不一致问题。
函数依赖
刚才的问题通过消除冗余数据和使用外键就能解决。有没有想过,解决方案背后的原理是什么呢?
核心在于,同一个产品始终有相同的单价(只要两个元组的产品ID相同,它们的产品单价也必须相同)。
一个属性决定另一个属性,这种约束就称为函数依赖。
函数依赖(Functional Dependency,FD)
- 是关系中两个属性之间的一组约束
- 表示如果两个元组在属性A1, A2,…, An上有相同的值,那么这两个元组必须在属性B1, B2,…, Bn上也有相同的值
- 函数依赖用箭头符号(→)表示,即X→Y,其中X函数决定Y。左侧的属性决定了右侧属性的值
简单来说,函数依赖X→Y,可以理解为相同的X一定有相同的Y。例如,同一个产品始终有相同的单价,表示为:{产品ID} → {产品单价}。
函数依赖的作用
函数依赖有什么作用?或者,为什么需要函数依赖?
例如,根据{产品ID} →{单价},可以将订单表{#订单号,产品ID,数量,单价},拆分成两个表:
- 单价表{#产品ID,单价},产品ID作为主键,确保每个产品ID是唯一的
- 订单表{#订单号,产品ID,数量},订单号作为主键,产品ID作为外键,关联到单价表
这么做有以下作用。
1. 保证数据一致性
- 插入一致性:当插入一个新的产品记录时,只要产品ID已经存在,系统将不允许插入,因为这会违反函数依赖,保证每个产品的单价信息是唯一的。
- 更新一致性:如果需要更新产品单价信息,只需通过产品ID定位到特定的单价记录进行更新。如果没有这样的函数依赖,可能会出现同一个产品的多条记录拥有不同的信息,导致数据不一致。
- 删除一致性:同理,删除操作也可以通过产品ID精确地移除某个产品单价信息,而不会影响其他产品单价数据。
2. 减少数据冗余
这种设计减少了数据的冗余存储,因为单价信息只存储一次,而不是在每个订单记录中重复存储。
3. 优化查询性能
例如,想查询产品单价,直接从单价表中查询即可,性能非常好(主键等值查询)。而在原始的订单表中查询某个产品的单价,,则需要执行过滤和去重操作。
4. 设计规范化的数据库
函数依赖是数据库设计规范化的基础方法(下篇文章介绍数据库规范化内容)。
函数依赖的性质
我们进一步讨论函数依赖具备的性质。
阿姆斯特朗公理(Armstrong’s Axioms)
阿姆斯特朗公理是一组规则,包括:
-
自反规则(Reflexive rule):如果α是一个属性集,β是α的子集,那么α→β。例如,如果我们有属性集{A, B},那么根据自反规则,我们可以得出函数依赖{A} → {A}和{B} → {B},因为每个属性自己就是自己的子集。
-
增广规则(Augmentation rule):如果a → b成立,并且y是一个属性集,那么ay → by也成立。也就是说,在依赖中增加属性,不会改变依赖关系。例如,如果我们有函数依赖{A} → {B},并且我们有另一个属性C,那么根据增广规则,我们可以得出函数依赖{AC} → {BC}。
-
传递规则(Transitivity rule):与代数中的传递规则相同,如果a → b成立并且b → c成立,那么a → c也成立。例如,如果我们有函数依赖{A} → {B}和{B} → {C},那么根据传递规则,我们可以得出函数依赖{A} → {C}。
平凡函数依赖(Trivial Functional Dependency)
函数依赖可以分为三种情况:
- 平凡 :如果一个函数依赖 X → Y成立,其中Y是X的子集,那么它被称为平凡函数依赖。平凡函数依赖总是成立的。
- 非平凡 : 如果 X → Y成立,其中Y不是X的子集,那么它被称为非平凡函数依赖。
- 完全非平凡 : 如果 X→Y成立,其中X与Y交集为空,则称为完全非平凡函数依赖。
自反规则和平凡函数依赖密切相关:
- 假设属性集{A,B},根据自反规则,{A,B} → {B}
- 因为{A,B} → {B},所以是平凡函数依赖
阿姆斯特朗公理和平凡函数依赖有什么用?
- 作为其它函数依赖的基础
- 用于推导出新的隐式的函数依赖
重新理解主键
函数依赖是一种表模式设计的基础方法,与主键有密切关系(一个属性如何决定其他属性),如何从函数依赖的角度理解主键?
从函数依赖的角度,可以这样理解主键:
- 表模式定义为:{X,Y},X和Y分别表示属性集(包含1~N个属性)
- 假设,X→Y成立,且Y包含除了X以外的所有其他属性,那么X就被认为是一个超键
- 如果X没有冗余,即不能再移除任何属性而保持其唯一性,则X可认为是候选键,可当选为主键
总结
通过函数依赖,我们知道应该如何分解关系模式,以保证数据一致性并消除数据冗余 — 即如何进行数据库规范化设计。
在规范化过程中,我们使用函数依赖来:
- 识别候选键。候选键必须满足以下条件:
- 候选键中的属性对关系中的所有其他属性都有函数依赖关系。
- 候选键中的属性没有冗余。
- 分解关系。一旦识别了候选键,就可以使用函数依赖来分解关系。分解涉及将关系拆分为多个较小的关系,每个关系都包含候选键。这有助于消除冗余并确保数据完整性。
更多有关数据库规范化的内容,我们下次再接着讨论。
系列文章
- 【如此简单!数据库入门系列】之初识数据库 – 数据库基本概念
- 【如此简单!数据库入门系列】之学霸数据库 – 数据库基本原理
- 【如此简单!数据库入门系列】之学霸数据库(2) – 数据库基本原理
- 【如此简单!数据库入门系列】之学霸数据库(3) – 数据库基本原理
- 【如此简单!数据库入门系列】之前世今生 – 数据库历史
- 【如此简单!数据库入门系列】之前世今生(2) – 数据库历史
- 【如此简单!数据库入门系列】之ER模型快速入门
- 【如此简单!数据库入门系列】之关系模型简介
如果喜欢这篇文章,请不要忘记关注、点赞和收藏哦!
您的鼓励将是我创作的最大动力!