本页目录

数据库设计三大范式

为了减少数据冗余,设计数据表时必须遵循一定的规则,在关系型数据库中这种规则就称为范式。
严谨的定义可以参考维基百科第一范式第二范式第三范式,互联网上也有很多优秀的技术博客。本文希望以简洁的语句和例子进行描述。

第一范式 - 1NF

数据库表的每一列都是不可再分的。

下面是一个不符合第一范式的例子:

人员编号

姓名

地址

职务

1001

李雨晨

江苏省南京市

财务部经理

1002

张文浩

辽宁省沈阳市

财务部员工

1003

王洪

湖北省武汉市

销售部实习生

1004

陈宇航

吉林省长春市

销售部员工

其中的职务应该再分为部门职务两个字段,因为业务中可能会经常分别用到这两个属性!

人员编号

姓名

地址

部门

职务

1001

李雨晨

江苏省南京市

财务部

经理

1002

张文浩

辽宁省沈阳市

财务部

员工

1003

王洪

湖北省武汉市

销售部实

习生

1004

陈宇航

吉林省长春市

销售部

员工

你可能在想地址字段是否应该拆分。甚至姓名也可以拆分成两个字段:

人员编号

部门

职务

1001

雨晨

江苏省

南京市

财务部

经理

1002

文浩

辽宁省

沈阳市

财务部

员工

1003

湖北省

武汉市

销售部实

习生

1004

宇航

吉林省

长春市

销售部

员工

这个例子比上一个更好吗?哪一种设计更符合第一范式?事实上,两个例子都是符合的,具体的取舍要参考具体的业务。例如姓名地址在业务中都整体出现,那么第二个例子就是最佳选择。如果业务涉及分别对所属省、市的操作,那么就应该将地址字段拆分为

第二范式 - 2NF

符合第一范式,且非主键字段必须完全依赖于主键字段,不能只依赖主键的一部分。(主要针对联合主键而言)

下面是一个不符合第二范式的例子:

学号(主键)

课程代码(主键)

学生姓名

成绩

202101

001

Alice

97

202102

001

Bob

86

202103

001

Candy

80

202103

003

Candy

93

这个例子中,成绩是针对某一学生的某一课程而言的,因此需要学号课程代码作为联合主键才能确定;然而学生的学生姓名仅由学号就可以唯一确定,这就叫部分依赖于主键,因此不符合第二范式。正确的做法是将学生姓名成绩分开存放于两张表中:

学号(主键)

学生姓名

202101

Alice

202102

Bob

202103

Candy

学号(主键)

课程代码(主键)

成绩

202101

001

97

202102

001

86

202103

001

80

202103

003

93

第三范式 - 3NF

符合第二范式,且非主键列都和主键直接相关,而不能间接相关(依赖于其他非主键字段)。

即第三范式要求不存在传递依赖,非主键属性之间应该是独立无关的。

下面是一个不符合第三范式的例子:

id

姓名

城市

城市人口/万

1

张三

重庆市

3100

2

李四

武汉市

1418

3

王五

北京市

2154

这个例子不符合第三范式,因为存在间接的传递决定关系:id → 城市 → 城市人口城市人口字段直接依赖的是城市字段,而不是主键id。正确的做法是设计两张表:

id

姓名

城市

1

张三

重庆市

2

李四

武汉市

3

王五

北京市

城市

城市人口/万

重庆市

3100

武汉市

1418

北京市

2154