小王刚学完 SQL,兴致勃勃地设计了一个学生信息表,字段包括:学号、姓名、班级、班主任姓名、班主任电话。他觉得挺全,查起来也方便——可老师一看就摇头:这表没‘规范’,迟早出问题。
什么是规范化?
规范化不是给数据库‘整容’,而是按一套逻辑规则,把数据拆得更干净、更合理。就像整理书架:不把小说、教材、工具书全塞进一个抽屉,而是分门别类,找书快、改书不乱、加书不卡壳。
第一范式(1NF):每个格子只装‘一个值’
先看这个反例:
学号 | 姓名 | 选修课程
001 | 张三 | 数学,英语,物理‘选修课程’一栏塞了三个课名,用逗号隔开——这就不符合第一范式。一旦想查‘谁选了英语’,就得用字符串函数硬拆,效率低还容易出错。
改成这样才合规:
学号 | 姓名 | 课程
001 | 张三 | 数学
001 | 张三 | 英语
001 | 张三 | 物理每行每列都只存单一、不可再分的值,查询、排序、索引才真正靠谱。
第二范式(2NF):非主键字段必须‘完全依赖’主键
再看小王那个学生表:
学号 | 姓名 | 班级 | 班主任姓名 | 班主任电话
001 | 张三 | 高三(2)班 | 李老师 | 138****1234
002 | 李四 | 高三(2)班 | 李老师 | 138****1234这里主键是‘学号’,但‘班主任姓名’和‘班主任电话’其实跟‘班级’挂钩,而不是单个学生。换言之,只要知道是‘高三(2)班’,就能确定班主任是谁、电话多少——它们‘部分依赖’于主键,违反了第二范式。
正确做法是拆成两张表:
学生表:
学号 | 姓名 | 班级
001 | 张三 | 高三(2)班
002 | 李四 | 高三(2)班
班级表:
班级 | 班主任姓名 | 班主任电话
高三(2)班 | 李老师 | 138****1234这样,班主任信息只存一次,改号码只需动一行;也不会出现同一个班两个班主任电话不一致的尴尬。
第三范式(3NF):别让‘间接依赖’偷偷搞事情
假设我们又加了一张教师表:
教师编号 | 姓名 | 所属院系 | 院系地址
T001 | 李老师 | 计算机系 | 教学楼A座301室‘院系地址’看起来合理?但它其实依赖于‘所属院系’,而‘所属院系’又依赖于‘教师编号’——这就是‘传递依赖’。万一计算机系搬去B座,就得挨个改所有计算机系老师的记录。
拆解后更清爽:
教师表:
教师编号 | 姓名 | 所属院系
T001 | 李老师 | 计算机系
院系表:
院系名称 | 地址
计算机系 | 教学楼A座301室地址只维护一次,牵一发不动全身。
规范化不是越拆越多就越好。实际开发中,有时为查得快,会适当‘反规范化’——比如把常用关联字段冗余存一下。但前提是:你清楚自己在做什么,以及为什么这么做。