数据库设计当中三范式是经常遇到的,如果实际项目数据库设计中能达到第三范式基本也就满足要求了,那么如何快速有效的理解三个范式,同时应用于实际项目中去呢?
首先看看标准定义的三个范式:
第一范式(1NF)
所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。
在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。
我的理解:列不可分。
第二范式(2NF)
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一的区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。要求实体的属性完全依赖于主关键字。
我的理解:不能部分依赖。即:一张表存在组合主键时,其他非主键字段不能部分依赖。
第三范式(3NF)
满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。
在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。
我的理解:不能存在传递依赖。即:除主键外,其他字段必须依赖主键。
官方标准的定义,我个人感觉说得非常术语化,比较难以理解消化。我简单的理解为三句话,非常简短,比较好理解。如果各位路过朋友们,有更好理解的总结,请不吝指出!
二,,
1.第一范式(确保每列保持原子性)
第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。
第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。
上表所示的用户信息遵循了第一范式的要求,这样在对用户使用城市进行分类的时候就非常方便,也提高了数据库的性能。
2.第二范式(确保表中的每列都和主键相关)
第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键,如下表所示。
订单信息表
这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。
而如果把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中,就非常完美了。如下所示。
这样设计,在很大程度上减小了数据库的冗余。如果要获取订单的商品信息,使用商品编号到商品信息表中查询即可。
3.第三范式(确保每列都和主键列直接相关,而不是间接相关)
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。如下面这两个表所示的设计就是一个满足第三范式的数据库表。
这样在查询订单信息的时候,就可以使用客户编号来引用客户信息表中的记录,也不必在订单信息表中多次输入客户信息的内容,减小了数据冗余。
三,,,
sql sever 中如何在两个表之间建立参照关系这个是通过创建一种称为 “外键” 的东西, 来建立 参照关系的。
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
-- 创建测试主表. ID 是主键.
CREATE
TABLE
test_main (
id
INT
NOT
<a href=
"https://www.baidu.com/s?wd=NULL&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1dWPWczPH0vnhnYmW6vrHD40ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1DdnH0knjmsPWR3PHb4PWc3PW0"
target=
"_blank"
class=
"baidu-highlight"
>
NULL
</a>,
value <a href=
"https://www.baidu.com/s?wd=VARCHAR&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1dWPWczPH0vnhnYmW6vrHD40ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1DdnH0knjmsPWR3PHb4PWc3PW0"
target=
"_blank"
class=
"baidu-highlight"
>
VARCHAR
</a>(10),
PRIMARY
<a href=
"https://www.baidu.com/s?wd=KEY&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1dWPWczPH0vnhnYmW6vrHD40ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1DdnH0knjmsPWR3PHb4PWc3PW0"
target=
"_blank"
class=
"baidu-highlight"
>
KEY
</a>(id)
);
-- 创建测试子表.
CREATE
TABLE
test_sub (
id
INT
NOT
<a href=
"https://www.baidu.com/s?wd=NULL&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1dWPWczPH0vnhnYmW6vrHD40ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1DdnH0knjmsPWR3PHb4PWc3PW0"
target=
"_blank"
class=
"baidu-highlight"
>
NULL
</a>,
main_id
INT
,
value <a href=
"https://www.baidu.com/s?wd=VARCHAR&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1dWPWczPH0vnhnYmW6vrHD40ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1DdnH0knjmsPWR3PHb4PWc3PW0"
target=
"_blank"
class=
"baidu-highlight"
>
VARCHAR
</a>(10),
PRIMARY
<a href=
"https://www.baidu.com/s?wd=KEY&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1dWPWczPH0vnhnYmW6vrHD40ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1DdnH0knjmsPWR3PHb4PWc3PW0"
target=
"_blank"
class=
"baidu-highlight"
>
KEY
</a>(id)
);
-- 插入测试主表数据.
INSERT
INTO
test_main(id, value)
VALUES
(1,
'ONE'
);
INSERT
INTO
test_main(id, value)
VALUES
(2,
'TWO'
);
-- 插入测试子表数据.
INSERT
INTO
test_sub(id, main_id, value)
VALUES
(1, 1,
'ONEONE'
);
INSERT
INTO
test_sub(id, main_id, value)
VALUES
(2, 2,
'TWOTWO'
);
下面这个语句, 在 主表与子表之间, 创建一个关系
ALTER
TABLE
test_sub
ADD
CONSTRAINT
main_id_cons
FOREIGN
<a href=
"https://www.baidu.com/s?wd=KEY&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1dWPWczPH0vnhnYmW6vrHD40ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1DdnH0knjmsPWR3PHb4PWc3PW0"
target=
"_blank"
class=
"baidu-highlight"
>
KEY
</a> (main_id)
REFERENCES
test_main;
|
四,,举个例子
用户(用户编号userid,用户名username,密码password,性别sex,权限authority,班级编号classid),主键是userid,外键是classid 学院(学院编号collegeid,学院名称college),主键是collegeid 专业(专业编号specialtyid,专业名称specialty,方向direction,学院编号collegeid),主键是specialtyid,外键是collegeid 年级(年级编号gradeid,年级名称grade),主键是gradeid 班级(班级编号classid,班级名称class,专业编号specialtyid,辅导员mother,班长monitor,年级编号gradeid),主键是classid,外键是专业编号specialtyid,辅导员mother(参照userid),班长monitor(参照userid),年级编号gradeid
五,,,好处
数据库的三范式对数据库来说是是具有一定好处的,先抛开定义不谈,每当设计数据库的时候,往往会具有较大的数据库体系,也就是每个表之间的关系,不同表中某些属性都具有一定的关系,因为具有关联,所以数据在存储的时候逻辑会比较复杂,如果不按照一定的规则来存储数据就会有乱子,就像你开车一样,没有交通规则不就混乱了么?因此,在数据库设计的时候满足范式要求可以很大限度的合理的处理数据,减少数据的冗余,即多余的用数据,当然范式的优点也不仅仅体现在这方面。 但换句话说,如果在某些特定的情况下还死死遵循范式也是不可取的,因为可能降低数据库的效率,数据库的设计应该根据当前情况和需求做出灵活的处理。