数据库范式Database Normalization

数据库范式化是为了最小化冗余和依赖,对关系型数据库中的字段和表进行合理组织的过程。

数据库范式有很多种,常见的有1nf,2nf,3nf和bcnf 4种:http://en.wikipedia.org/wiki/Database_normalization。如果是为了面试,掌握第一到第三范式就足够了。

第一范式是关系型数据库最主要的属性。也就是说第一范式是关系型数据库的最低要求。

如果一个关系型数据库满足第三范式,则称为“范式化的”。关系型数据库满足第三范式就可以了。

第一范式

A relation is in first normal form if the domain of each attribute contains only atomic values, and the value of each attribute contains only a single value from that domain.

即表中每个字段的值是原子的,不可再分的。

例如一个人设计了这样一个客户表:

Customer
Customer IDFirst NameSurnameTelephone Number
123RobertIngram555-861-2025
456JaneWright555-403-1659
789MariaFernandez555-808-9633

之后他意识到一个客户可能有多个电话号码。于是他修改了一下设计:

 

Customer
Customer IDFirst NameSurnameTelephone Number
123RobertIngram555-861-2025
456JaneWright555-403-1659
555-776-4100
789MariaFernandez555-808-9633

然而,如果电话号码字段是按照普通号码格式来设计的,例如字段长度为12个字符,那么上面的设计就不符合第一范式。事实上,任何一个关系型数据库系统都不允许一个字段存储多个值(注意上面的设计并不是将两个号码存成一个长字符串,而是说一个字段可以存储多个值)。因此我们不可能设计出违背第一范式的数据库。

符合第一范式的设计应该是设计两个表:

Customer Name
Customer IDFirst NameSurname
123RobertIngram
456JaneWright
789MariaFernandez
Customer Telephone Number
Customer IDTelephone Number
123555-861-2025
456555-403-1659
456555-776-4100
789555-808-9633

或者在customer表中,对于那些有两个号码的人,有两行记录,其中ID,Name等是重复的。

第二范式

表在满足第一范式之后,满足以下条件才能满足第二范式:表中的非关键字段要么依赖于candidate key,要么依赖于另外一个非关键字段。

No Partial Dependencies on a candidate Key( which also is a concatenated key).

所谓的candidate key可以理解为行的唯一key。

也就是说如果不满足第一范式,就不可能满足第二范式。

考虑这样一个员工技能表:

Employees' Skills
EmployeeSkillCurrent Work Location
JonesTyping114 Main Street
JonesShorthand114 Main Street
JonesWhittling114 Main Street
BravoLight Cleaning73 Industrial Way
EllisAlchemy73 Industrial Way
EllisFlying73 Industrial Way
HarrisonLight Cleaning73 Industrial Way

 

Employee字段和Skill字段都不合适作为行key。因为一个特定的员工可能出现多次,而一个特定的skill也可能出现多次。只有Employee和Skill的组合key才能唯一定位一行数据,在该表中只有Employee和Skill的组合key才是candidate key。

 

剩下的地址字段实际上是被Employee字段决定的。

因此该设计不符合第二范式:非关键字段Current Work Location依赖candidate key的一部分(即Employee)。

不符合第二范式的直接后果就是数据冗余,上例中很明显,地址信息是重复的。而数据冗余的直接后果就是可能出现数据不一致:

1,更新异常

假如由于某种原因,修改了Jones的skill为Typing那行对应的地址,但是没有修改另外两种skill对应的地址,数据就出现了不一致。

2,插入异常

如果插入一行Jones的新技能,但是地址不是114 Main Street,则数据出现了不一致。

将上例改为下面的设计使之符合第二范式,并消除了数据冗余:

Employees
EmployeeCurrent Work Location
Jones114 Main Street
Bravo73 Industrial Way
Ellis73 Industrial Way
Harrison73 Industrial Way
Employees' Skills
EmployeeSkill
JonesTyping
JonesShorthand
JonesWhittling
BravoLight Cleaning
EllisAlchemy
EllisFlying
HarrisonLight Cleaning

第三范式

Third Normal Form: No Dependencies on Non-Key Attributes 

下面这个例子满足第二范式,但不满足第三范式。该表存储的数据是几个不同的奖项和每年不同奖项各自的冠军。

Tournament Winners
TournamentYearWinnerWinner Date of Birth
Indiana Invitational1998Al Fredrickson21 July 1975
Cleveland Open1999Bob Albertson28 September 1968
Des Moines Masters1999Al Fredrickson21 July 1975
Indiana Invitational1999Chip Masterson14 March 1977

从表中可以得知candidate key是{Tournament,Year},而非关键字段Winner Data of Birth依赖于另一个非关键字段Winner,因此满足第二范式。但是仍然可以发现有数据冗余,并可能导致数据不一致。

第三范式的意思是表中的非关键字段只能依赖于candidate key,因此修改表结构为:

Tournament Winners
TournamentYearWinner
Indiana Invitational1998Al Fredrickson
Cleveland Open1999Bob Albertson
Des Moines Masters1999Al Fredrickson
Indiana Invitational1999Chip Masterson
Player Dates of Birth
PlayerDate of Birth
Chip Masterson14 March 1977
Al Fredrickson21 July 1975
Bob Albertson28 September 1968

范式设计与反范式设计

 

通过上文的介绍,我们可知在范式化的数据库中,每个事实数据(不包括自动生成的ID等列)会出现并且只出现一次(即没有冗余数据)。相反,在反范式化的数据库中,信息是冗余的,可能会存储在多个地方。

对schema进行范式化设计是推荐的设计基准,尤其是写密集的场景。范式化通常能够带来诸多好处:
1,范式化的更新操作通常比反范式化要快。原因很简单,范式化设计的数据库中数据没有冗余,因此数据量更小。

2,当数据较好地范式化时,就只有很少或者没有重复数据,所以只需要修改更少的数据。

3,范式化的表通常更小,可以更好地放在内存里,以及更快地备份恢复。

4,很少有多余的数据意味着检索列表数据时更少需要distinct或者group by语句。

范式化设计的schema缺点是通常需要关联。这不但代价昂贵,也可以使一些索引策略无效。反范式化的schema因为所有数据都在一张表中,可以很好的避免关联。
同时单独的表能使用更有效的索引策略。例如有一个网站,允许用户发送消息,且其中一部分用户是付费用户。现在想查看付费用户最近的10条信息。
如果是范式化的结构并且索引了发送日期字段published,这个查询也许看起来像这样:

select message_text, user_name
from message
inner join user on message.user_id=user.id
where user.account_type='premiumv'
order by message.published desc limit 10;


要有效地执行这个查询,有两种执行计划:
1, 扫描message表的published字段后,到user表中检查该用户是不是付费用户。但如果大部分用户都不是收费用户,则效率比较低下。
2, 或者从user表开始,选择所有的付费用户,获得他们所有的信息并且排序。但这可能更加糟糕。
主要问题是关联,使得需要在一个索引中又排序又过滤。如果使用反范式化的设计,在message表中加入user_type,则完全不会有这个问题。

 

因此我们在实际设计中,一般都是范式和反范式混合使用。在开始设计数据库时,应由范式化的设计开始,在大体结构定下之后,按照具体查询来做反范式化。如上例中,将user和message信息放在两个表中是合理的符合范式的设计,但如果有大量需要查询付费用户top10信息的查询,则应适当做反范式化设计。例如对于一些特定的表,使其符合第二范式但不符合第三范式。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值