📄 一句话概括
第三范式(3NF):非主键列之间不许互相依赖——非主键列 B 依赖另一个非主键列 A(A→B 的从属链),就把 A 和 B 一起搬走另立门户。这消灭的是「传递依赖」。
📝 本节和前面几节的关系
三台阶最后一阶,收拾 2NF 管不到的场合:单列主键表里,非主键列之间互相「抱团」。到达 3NF,业务表设计基本达标(5-10 的工程口径)。
一、问题表:列抱团
学生(学号 PK, 姓名, 系, 系电话)
- 姓名依赖学号 ✅(本职工作)
- 系电话:不看学号也不看姓名,它认的是「系」——非主键列依赖非主键列 ❌
依赖链:学号 → 系 → 系电话(电话传递地依赖学号)。
二、传递依赖的两宗罪
- 更新异常:一个系的学生 200 人,系里换电话改 200 行,漏一行就穿帮
- 插不进/删不净:新系还没招学生→整行无处安放;最后一个学生退学→系的信息跟着蒸发
三、整改:抱团的家伙一起搬走
students(学号 PK, 姓名, 系 FK)
departments(系 PK, 系电话) ← A 和 B 一起搬走,A 当主键
3NF 动作=找出「非主键 → 非主键」的依赖,把后者整族搬进新表,原表留外键。
四、动手 Demo:改一行的诱惑
-- 违规:电话抄在每个学生行里
CREATE TABLE bad_stu (id INT PRIMARY KEY, name VARCHAR(20), dept VARCHAR(20), dept_tel VARCHAR(20));
INSERT INTO bad_stu VALUES (1,'甲','数学','8801'),(2,'乙','数学','8801'),(3,'丙','外语','8802');
-- 数学系换电话:UPDATE 改 2 行(200 人呢?漏一行就是两个版本)
-- 3NF 整改
CREATE TABLE depts (dept VARCHAR(20) PRIMARY KEY, tel VARCHAR(20));
CREATE TABLE good_stu (id INT PRIMARY KEY, name VARCHAR(20), dept VARCHAR(20));
INSERT INTO depts VALUES ('数学','8801'), ('外语','8802');
-- 换电话:UPDATE depts 改 1 行,全系同步
五、30 秒实验
检查 orders(订单号 PK, 客户id, 客户姓名):客户姓名认谁?(认客户 id——非主键依赖非主键,传递依赖!)整改:客户姓名搬进 customers 表,订单表只留 customer_id 外键。这正是 5-10 Demo 里那张订单表的样子。
六、常见误区
- 「3NF 就是拆得越多越好」:只拆非主键间的依赖;随口起名「备注里带部门」之类弱关联不用草木皆兵
- 「到达 3NF 就高枕无忧」:业务表达标了;还有 BCNF 等更高范式,工程上少见,知道有即可
自测三问
1. 3NF 消灭什么?(传递依赖——非主键列依赖另一个非主键列) 2. 依赖链怎么读?(学号→系→系电话,电话传递依赖学号) 3. 整改动作?(A、B 整族搬进新表,A 当主键,原表留外键)- ☐ 下一节(5-14 数值类型、字符串类型、日期类型-确定分配给列的设置)
相关笔记
- 第二范式-拆分其他种类的项目 —— 上一节:第二台阶
- 规范化-统一数据的格式 —— 工程口径:业务表至少 3NF
- FOREIGN KEY、外键-与其他表进行关联 —— 留下的外键如何执法
- 🗄️ 数据库基础 · 索引 —— 全课路线图