vault backup: 2026-05-20 00:00:48

This commit is contained in:
hhs
2026-05-20 00:00:48 +08:00
parent 8c8c88c73f
commit ebf9db844d
4 changed files with 258 additions and 19 deletions
+37 -2
View File
@@ -40,16 +40,21 @@ CREATE TABLE employees (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
phone VARCHAR(20), -- 单一手机号
skills JSON -- 数组存在 JSON 字段内
skills JSON -- 数组存在 JSON 字段内(如 ["Go","Python"])
);
-- ❌ 违反 1NF:同一列存多个值(用逗号分隔)
CREATE TABLE bad_employees (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
skills VARCHAR(255) -- 'Go,Python,Docker' — 不行!
skills VARCHAR(255) -- 'Go,Python,Docker' — 不行!无法单独查询某个技能
);
```
> [!TIP] 现代变通方案
>
> MySQL 5.7+ 支持 JSON 类型。虽然从严格范式角度看 JSON 列内部仍有复合结构,但数据库引擎提供了高效的索引和查询能力(如 `JSON_EXTRACT`),所以在工程实践中被广泛接受。**核心原则不变:不要把非结构化数据当字符串拼接处理。**
1NF 是最基本的要求——每一列都是原子值,不能再拆分。现代关系型数据库默认强制执行 1NF。
### 第二范式(2NF)—— 消除部分函数依赖
@@ -133,6 +138,23 @@ CREATE TABLE student_course_teachers (
);
```
```sql
-- ✅ 修正为 BCNF:拆成两张表,让每个决定因素都是超键
CREATE TABLE courses_teachers (
course_id BIGINT PRIMARY KEY,
teacher_id BIGINT NOT NULL,
UNIQUE KEY uk_course (course_id) -- 一门课只有一位老师
);
CREATE TABLE student_grades (
student_id BIGINT,
course_id BIGINT,
grade DECIMAL(5, 2),
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (course_id) REFERENCES courses_teachers(course_id)
);
```
> [!QUESTION] 3NF 已经够了,为什么还需要 BCNF?
>
> 大多数工程场景 3NF 完全够用。BCNF 解决的问题通常涉及**多重候选键重叠**的复杂建模。如果你遇到 "一门课只能由一位老师教" 这样的约束,且该约束与你的自然主键冲突,才需要考虑 BCNF。实践中建议先做到 3NF,遇到更新异常再往上走。
@@ -142,6 +164,19 @@ CREATE TABLE student_course_teachers (
>
> 但这恰恰是现代工程实践的真相:我们用自增代理主键保证查询性能,用外键语义保证设计合理。**范式检查应该在业务层(逻辑模型)上做,而不是在物理表结构上硬抠。**
## 三范式速查总结
读完上面的详细内容,回头用这张表做最后的对比和记忆强化:
| 范式 | 核心问题 | 违规症状 | 一句话修复 |
|------|---------|---------|-----------|
| **1NF** | 列是不是原子值? | 一格里塞了多个值 | 拆成多行或多列 |
| **2NF** | 这列依赖主键的**全部**吗?(仅复合 PK 场景) | 某些列只依赖主键的一部分 | 把只依赖部分的列拆到新表 |
| **3NF** | 这列有没有**绕道**经过其他非主键列? | B 列的值由 C 列决定,而不是直接由主键决定 | 把 C 列及它决定的所有列拆出去 |
| **BCNF** | 每个决定因素都是超键吗? | 候选键之间有重叠,约束冲突 | 拆到没有交叉候选键为止 |
---
## 表设计实操流程
知道范式定义是一回事,拿到需求画出一张合理的 ER 图是另一回事。这里给一个**四步工作流**: