第一正规化(1NF,中国大陆译作第一范式,台湾译作第一正规化)是数据库正规化中所使用的一种正规形式。第一正规化是为了要排除 重复群 的出现,所采用的方法是要求数据库的每个字段都只能存放单一值,而且每笔记录都要能利用一个惟一的主键来加以识别。
目录[隐藏]
|
重复群通常会出现在会计帐上,每一笔记录可能有不定个数的值。举例来说:
顾客 | 日期 | 数量 |
---|---|---|
Pete | Monday | 19.00 -28.20 |
Pete | Wednesday | -84.00 |
Sarah | Friday | 100.00 150.00 -40.00 |
'数量' 就是所谓的重复群了,而在这种情况下这份资料就不符合第一正规化。想要消除重复群的话,只要把每笔记录都转化为单一记录即可:
交易 ID | 顾客 | 日期 | 数量 |
---|---|---|---|
1 | Pete | Monday | 19.00 |
2 | Pete | Monday | -28.20 |
3 | Pete | Wednesday | -84.00 |
4 | Sarah | Friday | 100.00 |
5 | Sarah | Friday | 150.00 |
6 | Sarah | Friday | -40.00 |
一样是在交易这个例子中,同一天同一个人买了同样的数量,这样的交易做了两次:
顾客 | 日期 | 数量 |
---|---|---|
Pete | Monday | 19.00 |
Pete | Monday | 19.00 |
如上所示,这两笔交易可以说是一模一样,也就是说如果只靠这些资料我们没有办法分辨这两笔记录。我们之所以说它不符合第一正规化,是因为上面这样的表示法欠缺一个唯一识别码,可以是一个字段,也可以是一组字段,而且可以保证在这个资料中唯一识别码不会重复出现。要将它正规化到符合第一正规化的原则只需要加入一个唯一识别码即可:
交易 ID | 顾客 | 日期 | 数量 |
---|---|---|---|
1 | Pete | Monday | 19.00 |
2 | Pete | Monday | 19.00 |
大多数的 RDBMS (关联式数据库) 允许使用者在定义资料表的时候不去指定主键,不过这么一来这种资料表就不符合第一正规化了。
从某个角度看来,不允许重复群的出现是关联式数据库表示资讯的方法,RDBMS 里资料表每一笔记录的每一个字段都只能有一个值。举例来说,如果定义了一个叫做 Favorite Number 的整数字段,每一笔记录的 Favorite Number 这个字段都只会是一个整数 (或是无);这也就是说,如果设定了主键的话,理论上不可能会有任何关联式数据库的资料表会违反第一正规化的原则。
不过就算是在这种情况下,还是可以设计出在骨子里违反第一正规化的资料表。最简单的方法就是把多个有意义的值编码过后存进一个字段里,然后在资料表中用很多字段来表达同一个事实。
在单一字段中存放多个值是违反第一正规化的做法,下面这个就是很好的例子,它把多个值用逗号分开来表示:
人 | 不喜欢的食物 |
---|---|
Jim | Liver, Goat's cheese |
Alice | Broccoli |
Norman | Pheasant, Liver, Peas |
以这样的设计看来,想要知道有什么人不喜欢某样特定的东西是很不容易的。不过可以把这个资料表转化成下面这种符合第一正规化的型式:
人 | 不喜欢的食物 |
---|---|
Jim | Liver |
Jim | Goat's cheese |
Alice | Broccoli |
Norman | Pheasant |
Norman | Liver |
Norman | Peas |
在同一个资料表里用多个字段来表达同一个事情也是违反第一正规化的:
人 | 喜欢的颜色 | 不喜欢的食物 (1) | 不喜欢的食物 (2) | 不喜欢的食物 (3) |
---|---|---|---|---|
Jim | Green | Liver | Goat's cheese | |
Alice | Fuchsia | Broccoli | ||
Norman | Blue | Pheasant | Liver | Peas |
Emily | Yellow |
就算我们能确定每个人不喜欢吃的食物最多不会超过三样,这还是一个很糟的设计。举例来说,我们想要知道所有不喜欢同一种食物的人的组合的话,这就不是件容易的事,因为食物有可能出现在任何一个字段,也就是说每一次的查询都要去检查 9 (3 x 3) 组不同的字段组合。
第二正规化(2NF,台湾译作第二正规化)是数据库正规化中所使用的一种正规形式。它的规则是要求资料表里的所有资料都要和该资料表的主键有完全相依关系;如果有哪些资料只和主键的一部份有关的话,就得把它们独立出来变成另一个资料表。如果一个资料表的主键只有单一一个字段的话,它就一定符合第二正规化。
一个资料表符合第二正规化当且仅当
有一个资料表记录了设备元件的资讯,如下所示:
元件 ID (主键) | 供应商 ID (主键) | 供应商名称 | 价格 | 供应商住址 |
---|---|---|---|---|
65 | 2 | Stylized Parts | 59.99 | VA |
73 | 2 | Stylized Parts | 20.00 | VA |
65 | 1 | ACME Industries | 69.99 | CA |
这个资料表的每个值都是单一值,所以它符合第一正规化。因为同一个元件有可能由不同的供应商提供,所以得把元件 ID 和供应商 ID 合在一起组成一个主键。
主键和价格之间的关系很正确:同一个元件在不同供应商有可能会有不同的报价,所以价格确实和主键完全相关(完全依赖)。
另一方面,供应商的名称和住址就只和供应商 ID 有关(部分依赖),这不符合第二正规化的原则。仔细看就会发现 "Stylized Parts" 这个名称和 "VA" 这个住址重复出现了两次;要是它改名了或是被其他公司并购了怎么办?这时候最好把这些资料存到第二个资料表中:
供应商 ID (主键) | 名称 | 住址 |
---|---|---|
1 | ACME Industries | CA |
2 | Stylized Parts | VA |
这么一来,原本的 "元件来源" 资料表就得要做相对应的更动:
元件 ID (主键) | 供应商 ID (主键) | 价格 |
---|---|---|
65 | 2 | 59.99 |
73 | 2 | 20.00 |
65 | 1 | 69.99 |
检查资料表里的每个字段,确认它们是不是都和主键完全相关,这样才能知道这个资料表是不是符合第二正规化;如果不是的话,就把那些不完全相关的字段移到独立的资料表里。接下来的步骤是要确保所有不是键的字段都和彼此没有相依关系,这就叫做第三正规化。
第三正规化(3NF,中国大陆译作第三范式,台湾译作第三正规化)是数据库正规化中所使用的一种正规形式,用来检验是否所有非键属性都只和候选键有相关性,也就是说所有非键属性互相之间应该是无关的。
第三正规化和第二正规化不同的地方在于,在第三正规化里,所有的非键属性都必须和每个候选键有直接相关。如果再对第三正规化做进一步加强就成了BC正规化,它所强调的重点就在于 "资料间的关系是奠基在键上、以整个键为考量、而且除了键之外不考虑其他因素"。
目录[隐藏]
|
令:
如果对于 这种型式的功能相依性而言,下列叙述任一为真的话,则可以称 符合第三正规化:
任何一个具有部份相依性或是转移相依性的关系都违反了第三正规化。
以下面这个定义机械元件的关系为例:
元件编号 (主键) |
制造商名称 | 制造商地址 |
---|---|---|
1000 | Toyota | Park Avenue |
1001 | Mitsubishi | Lincoln Street |
1002 | Toyota | Park Avenue |
本例中制造商地址很明显地不该被列在这个关系里面,因为和元件本身比起来,制造商地址应该和制造商比较有关系;正确的做法应该是把独立成为一个新的资料表:
制造商名称 (主键) |
制造商地址 |
---|---|
Toyota | Park Avenue |
Mitsubishi | Lincoln Street |
然后把原本的资料表改成这样:
元件编号 (主键) |
制造商名称 |
---|---|
1000 | Toyota |
1001 | Mitsubishi |
1002 | Toyota |
先前那个资料表的问题在于每提到一次制造商名称就要多存一次它的地址,而这就不符合第三正规化的原则。
下面提供了另一个例子:
订单编号 (Order Number) (主键) |
客户名称 (Customer Name) | 单价 (Unit Price) | 数量 (Quantity) | 小计 (Total) |
---|---|---|---|---|
1000 | David | $35.00 | 3 | $105.00 |
1001 | Jim | $25.00 | 2 | $50.00 |
1002 | Bob | $25.00 | 3 | $75.00 |
在本例中,非主键字段完全依赖于主键订单编号,也就是说唯一的订单编号能导出唯一非主键字段值,符合第二正规化。第三正规化要求非主键字段之间不能有依赖关系,显然本例中小计依赖于非主键字段单价和数量,不符合第三正规化。小计不应该放在这个资料表里面,只要把单价乘上数量就可以得到小计了;如果想要符合第三正规化的话,就把小计拿掉吧 (不过在做查询的时候,本来用 "SELECT Orders.Total FROM Order" 就要改成用 "SELECT UnitPrice * Quantity FROM Order" 了)。
订单编号 (Order Number) (主键) |
客户名称 (Customer Name) | 单价 (Unit Price) | 数量 (Quantity) |
---|---|---|---|
1000 | David | $35.00 | 3 |
1001 | Jim | $25.00 | 2 |
1002 | Bob | $25.00 | 3 |