数据库范式化是为了最小化冗余和依赖,对关系型数据库中的字段和表进行合理组织的过程。
现在共有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 ID | First Name | Surname | Telephone Number |
---|---|---|---|
123 | Robert | Ingram | 555-861-2025 |
456 | Jane | Wright | 555-403-1659 |
789 | Maria | Fernandez | 555-808-9633 |
之后他意识到一个客户可能有多个电话号码。于是他修改了一下设计:
Customer ID | First Name | Surname | Telephone Number |
---|---|---|---|
123 | Robert | Ingram | 555-861-2025 |
456 | Jane | Wright | 555-403-1659 555-776-4100 |
789 | Maria | Fernandez | 555-808-9633 |
然而,如果电话号码字段是按照普通号码格式来设计的,例如字段长度为12个字符,那么上面的设计就不符合第一范式。事实上,任何一个关系型数据库系统都不允许一个字段存储多个值(注意上面的设计并不是将两个号码存成一个长字符串,而是说一个字段可以存储多个值)。因此我们不可能设计出违背第一范式的数据库。
符合第一范式的设计应该是设计两个表:
Customer ID | First Name | Surname |
---|---|---|
123 | Robert | Ingram |
456 | Jane | Wright |
789 | Maria | Fernandez |
Customer ID | Telephone Number |
---|---|
123 | 555-861-2025 |
456 | 555-403-1659 |
456 | 555-776-4100 |
789 | 555-808-9633 |
第二范式
表在满足第一范式之后,满足以下条件才能满足第二范式:表中的非关键字段要么依赖于candidate key,要么依赖于另外一个非关键字段。
所谓的candidate key可以理解为行的唯一key。
也就是说如果不满足第一范式,就不可能满足第二范式。
考虑这样一个员工技能表:
Employee | Skill | Current Work Location |
---|---|---|
Jones | Typing | 114 Main Street |
Jones | Shorthand | 114 Main Street |
Jones | Whittling | 114 Main Street |
Bravo | Light Cleaning | 73 Industrial Way |
Ellis | Alchemy | 73 Industrial Way |
Ellis | Flying | 73 Industrial Way |
Harrison | Light Cleaning | 73 Industrial Way |
Employee字段和Skill字段都不合适作为行key。因为一个特定的员工可能出现多次,而一个特定的skill也可能出现多次。只有Employee和Skill的组合key才能唯一定位一行数据,在该表中只有Employee和Skill的组合key才是candidate key。
剩下的地址字段实际上是被Employee字段决定的。
因此该设计不符合第二范式:非关键字段Current Work Location依赖candidate key的一部分(即Employee)。
不符合第二范式的会带来的问题:
1,数据冗余
上例中很明显,地址信息是重复的。
2,更新异常
假如由于某种原因,修改了Jones的skill为Typing那行对应的地址,但是没有修改另外两种skill对应的地址,数据就出现了不一致。
3,插入异常
如果插入一行Jones的新技能,但是地址不是114 Main Street,则数据出现了不一致。
将上例改为下面的设计则符合第二范式:
Employee | Current Work Location |
---|---|
Jones | 114 Main Street |
Bravo | 73 Industrial Way |
Ellis | 73 Industrial Way |
Harrison | 73 Industrial Way |
Employee | Skill |
---|---|
Jones | Typing |
Jones | Shorthand |
Jones | Whittling |
Bravo | Light Cleaning |
Ellis | Alchemy |
Ellis | Flying |
Harrison | Light Cleaning |
第三范式
下面这个例子满足第二范式,但不满足第三范式。该表存储的数据是几个不同的奖项和每年不同奖项各自的冠军。
Tournament | Year | Winner | Winner Date of Birth |
---|---|---|---|
Indiana Invitational | 1998 | Al Fredrickson | 21 July 1975 |
Cleveland Open | 1999 | Bob Albertson | 28 September 1968 |
Des Moines Masters | 1999 | Al Fredrickson | 21 July 1975 |
Indiana Invitational | 1999 | Chip Masterson | 14 March 1977 |
从表中可以得知candidate key是{Tournament,Year},而非关键字段Winner Data of Birth依赖于另一个非关键字段Winner,因此满足第二范式。
但是仍然可以发现有数据冗余,同时可能导致更新异常。
而满足第三范式则可以完全避免这个问题:
Tournament | Year | Winner |
---|---|---|
Indiana Invitational | 1998 | Al Fredrickson |
Cleveland Open | 1999 | Bob Albertson |
Des Moines Masters | 1999 | Al Fredrickson |
Indiana Invitational | 1999 | Chip Masterson |
Player | Date of Birth |
---|---|
Chip Masterson | 14 March 1977 |
Al Fredrickson | 21 July 1975 |
Bob Albertson | 28 September 1968 |