范式化:1NF、2NF、3NF、BCNF
为什么需要范式化
问题:数据冗余导致更新异常
sql
-- 非范式化表
CREATE TABLE orders (
order_id INT,
user_name VARCHAR(100),
user_email VARCHAR(255),
product VARCHAR(100),
amount DECIMAL(10,2)
);问题:
- 更新异常:用户改邮箱需要更新多行
- 插入异常:无法插入没有订单的用户
- 删除异常:删除订单会删除用户信息
第一范式(1NF)
定义:每列都是原子值(不可再分)。
违反 1NF
sql
CREATE TABLE orders (
id INT,
products VARCHAR(255) -- 'Laptop, Mouse, Keyboard'
);符合 1NF
sql
CREATE TABLE orders (
id INT,
product VARCHAR(100) -- 每行一个产品
);第二范式(2NF)
定义:符合 1NF,且非主属性完全依赖于主键。
违反 2NF
sql
CREATE TABLE order_items (
order_id INT,
product_id INT,
product_name VARCHAR(100), -- 只依赖 product_id
quantity INT,
PRIMARY KEY (order_id, product_id)
);符合 2NF
sql
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_id)
);
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100)
);第三范式(3NF)
定义:符合 2NF,且非主属性不依赖于其他非主属性。
违反 3NF
sql
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
user_name VARCHAR(100), -- 依赖于 user_id
user_email VARCHAR(255) -- 依赖于 user_id
);符合 3NF
sql
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(255)
);BCNF(Boyce-Codd 范式)
定义:符合 3NF,且每个决定因素都是候选键。
在大多数情况下,3NF 已经足够。
反范式化
为了提高查询性能,有时故意引入冗余。
场景:频繁查询用户名
sql
-- 范式化(需要 JOIN)
SELECT o.id, u.name FROM orders o JOIN users u ON o.user_id = u.id;
-- 反范式化(直接查询)
SELECT id, user_name FROM orders;权衡:
- 优点:查询更快
- 缺点:更新复杂,数据可能不一致
实际设计原则
- 先范式化:消除冗余
- 根据需求反范式化:提高性能
- 使用视图:隐藏复杂性
- 使用触发器:维护冗余数据一致性
练习
- 识别违反 1NF 的表
- 将违反 2NF 的表范式化
- 将违反 3NF 的表范式化
- 设计电商系统的表结构(用户、订单、产品)
- 设计博客系统的表结构(用户、文章、评论、标签)
- 分析何时应该反范式化
- 使用视图简化范式化表的查询
- 设计完整的项目数据库(至少 5 个表)
完成练习后,打开本章答案页核对。
本章小结
第七章学习了数据定义与约束:
- DDL 操作:CREATE、ALTER、DROP
- 数据类型:整数、字符串、日期时间、JSON
- 约束:PRIMARY KEY、FOREIGN KEY、UNIQUE、CHECK
- 范式化:1NF、2NF、3NF、BCNF
至此,SQL 七章全部完成!