数据库范式
关系型数据库中的4种键:
超键(super key):在关系中能惟一标识元素属性的集称为关系模式的超键。
候选键(Candidate Key):不含有多余属性的超键称为候选键。也就是说如果在候选键中继续继续删除属性,就不是键了。
主键(Primary Key):用户选作元组标识的候选键为主键。一般如果不加说明,键就是主键。
外键(Froeign Key):如果模式$R$中的属性$K$是其他模式的主键,那么$K$在模式$R$中称为外键。
condition | 1NF | 2NF | 3NF | 备注 |
---|---|---|---|---|
主键 | o | o | o | 没有完全重复的元组,若有则需加上唯一标识符 |
无重复组 | o | o | o | 一个方框中只有一条记录,否则需要拆分为2个元组 |
字段原子性 | o | o | o | 字段不可再分 |
没有部份依赖函数 | x | o | o | 数据表里的所有数据都要和该数据表的键(主键与候选键)有完全依赖关系 |
没有传递函数依赖 | x | x | o | 所有非键属性都只和候选键有相关性,也即非键属性之间应该是独立无关的 |
注:o表示当前范式对该condition做要求,x表示当前范式对该condition不做要求。
第一范式
第一范式(1NF)是数据库正规化所使用的正规形式。第一范式是为了要排除重复组的出现,所采用的方法是要求数据库的每个列的值域都是由原子值组成;每个字段的值都只能是单一值。
主键
Customer | Date | Amount |
---|---|---|
Pete | Wednesday | -84.00 |
Pete | Wednesday | -84.00 |
上表中,有人在同一天有相同的交易金额,缺乏唯一识别码(主键),元组无法被唯一区分,应做如下调整:
Transaction ID | Customer | Date | Amount |
---|---|---|---|
1 | Pete | Wednesday | -84.00 |
2 | Pete | Wednesday | -84.00 |
重复组
Customer | Date | Amount |
---|---|---|
Pete | Monday | 19.00 -28.20 |
Pete | Wednesday | -84.00 |
Sarah | Friday | 100.00 -40.00 |
上表在Amount
列上有重复组,违反了第一范式,应改为:
Customer | Date | Amount |
---|---|---|
Pete | Monday | 19.00 |
Pete | Monday | -28.20 |
Pete | Wednesday | -84.00 |
Sarah | Friday | 100.00 |
Sarah | Friday | -40.00 |
字段原子性
即字段不可再分原则。
第二范式
第二范式(2NF)是数据库正规化所使用的正规形式。规则是要求数据表里的所有数据都要和该数据表的键(主键与候选键)有完全依赖关系:每个非键属性必须独立于任意一个候选键的任意一部分属性。如果有哪些数据只和一个键的一部分有关的话,就得把它们独立出来变成另一个数据表。如果一个数据表的键只有单个字段的话,它就一定匹配第二范式。
一个数据表匹配第二范式当且仅当:
- 它匹配第一范式
- 所有非键的字段都一定是候选键全体字段的函数
没有部份依赖函数
所有非键的字段都一定是候选键全体字段的函数
考虑如下数据表:
组件ID(主键) | 价格 | 供应商ID(主键) | 供应商名称 | 供应商住址 |
---|---|---|---|---|
65 | 50.99 | 1 | Stylized Parts | VA |
73 | 20.00 | 1 | Stylized Parts | VA |
65 | 69.99 | 2 | ACME Industries | CA |
这个数据表的每个值都是单一值,所以它匹配第一范式。因为同一个组件有可能由不同的供应商提供,所以得把组件 ID 和供应商 ID 合在一起组成一个主键。
组件(关键词)和价格之间的关系很正确:同一个组件在不同供应商有可能会有不同的报价,所以价格确实和主键完全相关(完全依赖)。
另一方面,供应商的名称和住址就只和供应商 ID 有关(部分依赖),这不匹配第二范式的原则。仔细看就会发现 “Stylized Parts” 这个名称和 “VA” 这个住址重复出现了两次;要是它改名了或是被其他公司并购了怎么办?这时候最好把这些数据存到第二个数据表中:
供应商 ID (主键) | 名称 | 住址 |
---|---|---|
1 | Stylized Parts | VA |
2 | ACME Industries | CA |
这么一来,原本的 “组件来源” 数据表就得要做相对应的改动:
组件ID(主键主键) | 价格 | 供应商ID(主键) |
---|---|---|
65 | 50.99 | 1 |
73 | 20.00 | 1 |
65 | 69.99 | 2 |
检查数据表里的每个字段,确认它们是不是都和关键词完全相关, 这样才能知道这个数据表是不是匹配第二范式; 如果不是的话,就把那些不完全相关的字段移到独立的数据表里。 接下来的步骤是要确保所有不是键的字段都和彼此没有相依关系,这就叫做第三范式。
第三范式
第三范式(3NF)是数据库正规化所使用的正规形式,要求所有非键属性都只和候选键有相关性,也就是说非键属性之间应该是独立无关的。
正规定义
令:
- $R$ 表为一个关系
- $F$表维持$R$所需的一组函数依赖
- $X$表为$R$属性的子集合
- $A$表为$R$的一个属性
如果对于 $X -> A$ 这种型式的函数依赖而言,下列叙述任一为真的话,则可以称 $R$ 匹配第三范式:
- $A \in X$;也就是说$A$是明显函数依赖
- $X$是超键
- $A$是$R$的候选键的一部分
任何一个具有部分相依性或是转移相依性的关系都违反了第三范式。
以下面这个定义机械组件的关系为例:
组件编号(主键) | 制造商名称 | 制造商地址 |
---|---|---|
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 Order.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 |