一句话定义
函数依赖(Functional Dependency,X → Y)刻画「X 相同则 Y 必相同」的数据约束,范式(Normal Form)则依据依赖关系把大表无损分解为小表,以消除插入、更新、删除三类异常。
为什么重要
- 冗余不是「多占点空间」的问题:同一事实存多处,更新时漏改一处即产生脏数据,对账与排查成本极高。
- 范式给了设计与评审一个客观标准:能写出依赖关系、算出候选键、指出违反之处,评审就从「感觉不舒服」变成「可证明」。
- 反范式的前提是懂范式:kp-008 的一切权衡都以本知识点为地基。
前置知识
kp-006(关系模式、候选键、设计流程)。
核心概念
- 函数依赖 X → Y:任意两元组在 X 上相等则必在 Y 上相等。
- 平凡依赖:Y ⊆ X 的依赖(恒成立,无信息量)。
- Armstrong 公理:自反(若 Y ⊆ X 则 X → Y)、增广(X → Y 则 XZ → YZ)、传递(X → Y、Y → Z 则 X → Z)。
- 属性闭包 X⁺:由 X 出发用公理能推出的全部属性集合;X⁺ = 全部属性 ⟺ X 是超键。
- 范式阶梯:1NF(属性原子)→ 2NF(消除非主属性对候选键的部分依赖)→ 3NF(消除传递依赖)→ BCNF(每个非平凡依赖的决定方都是超键)。
- 无损分解:分解后的表按公共属性自然连接能还原原表;依赖保持:原依赖都能在分解后的表上检验。
原理与机制
三类异常同源于冗余:设表 orders(order_id, user_name, user_city) 存在 order_id → user_id → user_name, user_city。更新异常:同一用户改名需改多行;插入异常:新用户没有订单就进不了表;删除异常:删掉某用户全部订单,其信息随之消失。分解方法:先求候选键(从「只出现在依赖右部」的属性开始判断闭包),再对每个违反 BCNF 的非平凡依赖 X → Y,把模式拆成 (X ∪ Y) 与 (R − Y),重复直至全部满足。3NF 与 BCNF 的差异:3NF 允许「右部是主属性」的依赖存在,因此 3NF 总能同时做到无损与依赖保持,而 BCNF 只保证无损、可能丢依赖——这是考试与工程里都必须讲清的经典取舍。
实例或案例
对 R(student, course, teacher, teacher_office),依赖 teacher → teacher_office、(student, course) → teacher。候选键为 (student, course)。teacher_office 非主属性,通过 teacher 传递依赖候选键,违反 3NF。分解:R1(teacher, teacher_office)、R2(student, course, teacher),两表均为 BCNF,连接键 teacher 无损还原,且每个依赖都能落在单表内检验(依赖保持)。
常见误区
- 认为「列不多就不用管范式」:冗余的代价是数据不一致而非空间;三列的表照样可能违反 BCNF。
- 把 BCNF 当万能终点:BCNF 可能无法依赖保持(如城市→街道类依赖),此时工程上常退守 3NF。
- 为了性能直接堆冗余列却不写清楚同步机制:反范式必须显式说明「谁负责维护冗余数据」(见 kp-008)。
自测题
- 如何验证 X 是否为超键? 答:计算属性闭包 X⁺,若 X⁺ 包含 R 的全部属性则 X 是超键;若 X 的任何真子集闭包都不全,则 X 是候选键。
- 3NF 与 BCNF 的核心差别? 答:3NF 允许「决定方不是超键但右部是主属性」的依赖,从而总能无损且依赖保持地分解;BCNF 更严格、可能失去依赖保持。
公式或模型
闭包算法(多项式时间):令 S = X;反复扫描依赖集 F,若存在 A → B 且 A ⊆ S,则 S = S ∪ B,直到 S 不再增大;S 即 X⁺。判超键:X⁺ = R。判 BCNF:对每个非平凡 X → A,验证 X⁺ 是否等于 R。
图示
本节不适用:依赖关系用文字与算法步骤表达已足够精确,画依赖图易与 ER 图混淆。
直观类比
范式分解像整理重复登记的通讯录:原来每张借条上都抄着「张三的地址」,张三搬家后一半借条是旧地址。把「人 → 地址」抽成一本独立通讯录,借条上只留姓名——抄写错误(更新异常)从结构上消灭了。
与其他知识点的关系
kp-006 提供被检验的关系模式;kp-008 是「何时故意不分解」的工程答案;kp-024 讨论分区后跨分片的依赖与连接如何处理。
延伸阅读
- 《数据库系统概念》(Abraham Silberschatz 等)规范化章节
- C. J. Date《数据库系统导论》(An Introduction to Database Systems)关于范式的严格讨论