首页 > 代码库 > SQL 范式(转载)

SQL 范式(转载)

装载于"http://www.cnblogs.com/KissKnife/

理论性的东西,往往容易把人人都看得懂的东西写成连鬼都看不懂,近似于主任医生开的药方。从前学范式的时候,把书中得概念翻来覆去看,看得痛心疾首深恶痛绝,再加上老师深切误导,最后一塌糊涂。借助网络资源,自己写了一篇,自己是看懂了,希望对大家也有所帮助,有错误帮忙指正。

数据库范式(Normal forms):是用于规范关系型数据库设计,以减少谬误发生的一种准则。

1NF(first normal form) 

Table faithfully represents a relation and has no repeating groups.

数据库表必须如实地展现“关系”,并且不允许有“重复组”出现。

这样的概念真是令人痛心疾首,我们只好再搬出1NF的的作者之一Chris Date的解释:

1. There‘s no top-to-bottom ordering to the rows.

(任意两行没有特定的顺序关系。不存在一个特定的理由要某一行必须在另一行之前。)

2. There‘s no left-to-right ordering to the columns.

(任意两列没有特定的顺序关系。)

3. There are no duplicate rows.

(不允许存在重复的行。如果一张表没有Unique Key,事实上它是违反1NF。)

4. Every row-and-column intersection contains exactly one value from the applicable domain (and nothing else).

(不允许出现空值Null,这一点不同作者是有争议的。事实上我们常常违背这点。)

5. All columns are regular [i.e. rows have no hidden components such as row IDs, object IDs, or hidden timestamps].

(不允许存在隐藏字段。不知道Oracle的Rowid属不属于这个?)

有人从第四点的“one value”大肆挖掘,于是我们就见到了书上这样的定义:“如果一个关系模式R的所有属性都是原子的,即不可再分的基本数据项,则R?1NF”。

这一点被认为是1NF的核心,“关系模式R”?“表”,“属性” ? “列”,下面是一种与1NF不一致的情况,通常这是一类很明显的设计缺陷:

ID

Artist

FavoriteColor

…… 

1

Babyface

Blue,Yellow

……

2

Sting

Green

……

对上例我们不能把它拆分成FavoriteColor1、FavoriteColor2……因为首先我们不能确定该拆分成几列;其次FavoriteColor1与FavoriteColor2在结构、含意方面都是相同的,这实际上也是一类“repeating group”;同时这种设计会导致某些查询困难,比如“有哪些艺人喜欢黄色?”

解决方案是将表拆分成两个:

ID

Artist

…… 

1

Babyface

……

2

Sting

……

ID

FavoriteColor

1

Blue

1

Yellow

2

Green

总结: 

1NF最核心的 “原子性”,违反此规范的可能性:接近于0%。不过,网上很多帖子说在关系型数据库中根本不可能违背1NF,我认为这是不对的。 

2NF(second normal form):

No non-prime attribute in the table is functionally dependent on a part (proper subset) of a candidate key.

不存在非主属性对任一候选键的部分函数依赖。

如果解释完下面几个概念,这个定义就可以读懂了:

Superkey:超级键(L),如果属性或属性组合能唯一标识一条记录,则它是一个Superkey。

Candidate key:候选键,当Superkey只包含一个属性时,则它是一个候选键;当Superkey包含一组属性时,仅当这一组属性不包含另一Superkey时,它是一个候选键。换句话说,候选键是“纯净的”、最小化的Superkey。

Non-prime attribute:非主属性,未在任何候选键中出现的属性,即为非主属性。

举例来说,对表{First_name,Last_name,Address},假定全名不重复,则:

Superkey:

{First_name,Last_name}

{First_name,Last_name,Address}

Candidate key:

{First_name,Last_name}

Non-prime attribute:

Address

浅白版:“2NF针对的是复合候选键(即键包含的字段个数>1)的情况,非主属性不能只依赖于复合候选键中的一部分字段。”显然,如果是非复合候选键,如果它符合1NF,那么它一定符合2NF。

假设有这样一张涉及艺人与唱片公司的关系表:

Artist

艺人 

Company

唱片公司 

DurationYears

签约总年数 

CompAddr

公司住址 

Babyface

Solar

4

Indiana

Babyface

Laface

2

Indiana

       

显然,{Artist,Company}为可以作为一个候选键,DurationYears在这没有问题,但CompAddr是违反2NF的,它只依赖于候选键的一部分(依赖于Company),这是违反2NF的,为了消除这种情况,我们可以:

Artist

艺人 

CompID

唱片公司 

DurationYears

签约总年数 

Babyface

1

4

Babyface

2

2

ID

Company

唱片公司 

CompAddr

公司住址 

1

Solar

Indiana

2

Laface

Indiana

总结: 

对于2NF,如果关系中的候选键只包含一个属性,可以直接略过。 

 

在考虑2NF的过程中,不要把几个无关的实体的属性杂揉放在一个关系中,比如Artist是一个实体、Company是一个实体,它们可以有一系列的关联表(也是实体),但在关联表中尽量不要引入前两个实体的无关属性。 

3NF(Third normal form)

Every non-prime attribute is non-transitively dependent on every key of the table.

不存在非主属性对任一键(候选键)的传递依赖。

传递依赖,你可以顾名思义,这里就不再引入定义了,举个例子,有下面一张表:

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

这里的候选键为{Tournament,Year},显然有这样的决定关系:

{Tournament,Year}→Winner

{Tournament,Year}→Winner→Winner Date of Birth

其中第二条就属于违反3NF的情况,因为Winner Date of Birth依赖于Winner而不是直接依赖于候选键。这种情况下,可以将Winner,Winner Date of Birth单独作为一张表,这里不赘述。

总结: 

我觉得大多数人凭借直观感觉,就可使设计的关系符合3NF,所以这些理论,你只需要姑且读之。 

BCNF(Boyce-Codd normal form)BoyceCodd是该范式的两名作者。) 

Every non-trivial functional dependency in the table is a dependency on a superkey.

表中的任何非平凡函数依赖,都必须是对superkey的依赖。

non-trivial functional dependency:非平凡函数依赖,如果存在一个决定关系x→y,且y并非x的子集,则叫着y非平凡函数依赖于x。

BCNF与3NF的最大区别是它并不仅针对非主属性(non-prime attribute)来说,它发生的时候常常是表中根本不存在非主属性,以至于它不可能违反2NF或3NF。而BCNF的出现就是为了扩大“打击面”。

于是BCNF的主旨是:补充对发生在主属性(prime attribute)身上的函数依赖的约束,因为对于非主属性的约束已经在3NF中完成了。

例子,使用关系表描述学生、课程、教师的关系(假定一名教师只负责一门课程,一门课程则可以由多位教师负责):

Student

学生 

Course

课程 

Teacher

教师 

S1

C1

T1

S1

C2

T2

S2

C1

T1

S2

C2

T3

S2

C3

T2

候选键:

{Student,Course}

{Student,Teacher}

因此这里不存在非主属性,而在主属性的函数依赖中,存在Teacher→Course,这属于违反BCNF的情况。

可是,问题是这个表看起来还挺正常的啊?!它的毛病在于,我们无法阻止类似最后一行这样的数据插入,而这会导致与前提“一名教师只负责一门课程”违背。所以我们还是需要将它拆分:

Student

学生 

Teacher

教师 

S1

T1

S1

T2

S2

T1

S2

T3

Teacher

教师 

Course

课程 

T1

C1

T2

C2

T3

C2

这样,在“Teacher-Course”表中,借助主键的帮助,最后可以避免违背“一名教师只负责一门课程”这个前提。

那么,如果没有这样一个前提,是初的设计是否符合BCNF?目前看来是的。

真实的情况可能更为复杂,下面这个更接近于我的一些经历:

1)学生需要学习多门课程

2)一门课程可能有多位教师负责

3)一位教师可能负责多门课程

4)某一班级的某一课程对应的教师是固定的(一位)

据此,为了描述学生、课程、教师三者的关系,从这一团乱麻中最早跳出来的大概是这样的表:

Student

学生 

Class

班级 

Course

课程 

Teacher

教师 

       
       

候选键:

{Student,Course}

我们可以明显地看到Student→Class违反了2NF,于是:

Student

学生 

Class

班级 

   
   

Class

班级 

Course

课程 

Teacher

教师 

     
     

从这两张表,仔细考虑,即便我们通过Class关联两张表,还是无法得出学生与课程的关系(只能得出可供该学生选择的课程),所以我们需要再添加一张表:

Student

学生 

Course

课程 

   
   

最后大概是这么三张表,可能还有其它的方案,这里只是举例说明,就不纠缠了。

在BCNF之后,还有4NF,5NF,DKNF,6NF,等什么时候有空了再看看是什么东东。

"

再转一篇百度文库里的文章, 没有上篇那么通俗易懂但是内容很全

"

范式 

就关系数据库而言,一贯认为:从其他元素中消除数据冗余问题,去除重复往往以减少冗余, 从特定的表中最小化冗余意味着摆脱不必要的数据。

商业上来讲,主要目标是通常保存空间和组织的数据可用性和可管理性,而不牺牲性能。此外,要求强烈繁忙的应用程序和最终用户的需要往往需要以多种方式打破规则的范式,以满足性能要求。第三范式以外的范式常常被忽视和有时甚至是第三范式本身就是多余的。

范式是一个升级的过程,每个上层的模式都是建立在下一级范式之上的。

消除数据冗余的影响如下:

 ?物理空间需要存储的数据减少。

 ?数据变得更有组织。

 ?范式化允许修改少量的数据(即单记录)。换言之,一个表的具体字段记录更新时,会影响其他引用他的表。

首先我们对一些概念性的东西来进行一个总结,通过对这些概念的理解,从来从根本上做到合理的数据库设计:

 

异常

  ?  添加异常:当我们添加一条记录的时候,他依赖的主表记录还没有记录,而该记录已经插入成功。

  ?  删除异常:当我们的主表记录删除,而依赖他的子表没有清空对应的记录。

  ?  更新异常:当我们的主表记录有更新草组,而已来他的子表没有相应的更新记录。依赖,决定因子

 

  ?  函数依赖:当Y的值由X决定的时候,我们就说Y函数依赖于X,这就类似于一个线性方程:Y=X+1;类似的ERD图中,我们这样表示 ,很清楚的看到表Category,中的主键是CategoryID,他决定着其他字段的值Name和Pic,我们就说Name或者Pic 函数依赖于CategoryID,他们之间就是一个函数依赖关系。

  ?  决定因素:如上例中,CategoryID就是一个决定因素,他决定其他字段的值,Y=X+1中,X就决定着Y的值,虽然加了一个常量。

  ?  传递依赖:当X决定Y,Y决定Z的时候,我们就说Z传递依赖于X,,从这个ERD图中,我们看到Account帐号表中的City字段,他被AccountID所决定,而City字段的值又决定了College的值,因为大学肯定是被城市所决定,所以College就传递依赖于AccountID。

  ?  候选键:候选键(潜在的或允许的主键)可以扮演主键的角色他可以是一个表中的一个字段或组合字段——也就是一条记录中的唯一标识。,我们看到这个表,Customer表(客户表),字段分别表示,客户ID,客户名,货币缩写码,货币,转换汇率,地址。这个表我们没有定义主键,但是我们可以推测那些可以成为主键,那么那些键就叫做候选键。,我们就看到了,所有能成为主键的可能,表中的#就是主键的标识符。

  ?  完全函数依赖:当X决定Y,但是X不被X和Z的组合所决定,换句话说,YE依赖于独立的X,如果Y依赖于X加上一些其他的东西,那就不是完全函数依赖,本质上,决定因素X不能是一个组合键。,我们来看到旅游表Travel,Country是旅游的国家,Populication是旅游的城市,同时TravelID和CountryID是主键,可以看出来只有CountryID决定着Populcation人口,但是这里有两个主键,所以Populication并没有完全函数依赖于主键组合,只部分依赖于CountryID

  ?  多值依赖:某个字段中的值之一个集合,或者是用某种分隔符分割开来的元素集合,我们就称为多值依赖。很经典的,Path保存的这个递归表的所有上司的级别,比如老大的ID是1,老2是2,Path 就保存1,2,像这样的字段,我们就称为多值依赖。

  ?  循环依赖:循环依赖就如其名,是一个个闭环的依赖系统。A依赖于B,B依赖于C,C依赖于A。范式(学术定义)

 

  ?  第一范式(1NF):消除表中所有重复的记录,除了主键以外的所有其他字段全部依赖于主键。

  ?  第二范式(2NF):所有非键值字段必须全部完全函数依赖于主键,当一个字段完全函数依赖于一组组合主键的部分函数依赖是不允许的。

  ?  第三范式(3NF):消除传递依赖,意味着一个字段必须非间接的依赖于主键?  正规化范式(BCDF):所有表中的决定因素必须是一个候选键,如果只有一个候选键,那么就和第三范式是一样的。

  ?  第四范式(4NF):消除多值依赖。

  ?  第五范式(5NF):消除循环依赖。

 

我们从一个比较容易的位置来理解范式,通过上述的理解,加上实际的操作范式,让我们对数据库的设计有一个比较深的认识,以决定什么情况下用范式。

  ?  第一范式(1NF):通过创建一个新表来移除重复的元素,使他们成为一个主从关系或者是one to many的关系,类似下图:。

  ?  第二范式(2NF):建立在1NF的基础上,就是移除重复的值到一个新表中去,新表有唯一的主键,而主表有一个对新表的外键的引用,排除存在的部分依赖。如下图:Bookid 是book表的主键,而author是author1的主键,在book表中建立author,主表有一个对子表的外键引用,而不是把author也定义为主键,那样就存在部分函数依赖,因为bookid就已经确定了title,page,isbn等等信息。

  ?  第三范式:消除传递依赖,如下图:我们把多对多的关系变化成上图的关系。这是一种简单的形式,下面展示一个另外一种情况:这里的表分别是客户(客户名,货币号,货币,货币,汇率,地址),提供商(客户名,货币号,货币,汇率,地址)。首先他们的地址决定了他们的货币情况,而地址又是由客户或者提供商决定的,所以他们之间存在一个传递依赖关系,而且最好是把相同的存在于不同的数据移植到一个新表中去。前面我们提到了这个关系,也是一个不满足第三范式的表,City和College以及主键存在传递关系,所以可以把College移植到一个新表中去,还有一些存在订单的表中,有类似(qty(数量),price(单价),total(总价))的结构,qty和price决定了total,而订单号决定了qty和,price,所以也存在一中依赖关系,我们要删除total字段,但是不是都遵循范式的表都是好的结构,我们还是要根据实际情况,比如在一个数据仓库的设计中,汇总字段就是必须的。

  ?  超三范式(Beyond 3NF)中的one to one关系,这个主要用户当我们表中一些字段经常存在空值的时候,我们将存在的NULL字段移到一个新表中去,然后建立1对1的关系。

  ?  BCNF范式:BCNF划分成多个表的表格,以确保没有一个单一的表有更多不止一个潜在的主键。这是我的理解BCNF 。在我看来,是BCNF 用于商业环境是“过度设计“的。从本质上讲,从数学的角度上它的漂亮,但在商业环境中它不是很酷。下面的例子是一个BCNF转换:

  ?  第四范式:消除多值依赖,很经典的一个环境就是,我们的递归表问题,一个Manager有多个Employee,然后每条记录都有一个Path老表示这种关系,所以path和EmployeeId就存在一个多对多的关系。我们就应该重新建立一个新表,用来消除Path字段,新表中就保留一个EmployeeId作为外键,另外一个EmployeeId用来表示他的下属。

  ?  第五范式:消除循环依赖,如下图:在这个实例中Solution表中的键都是主键,他们存在这样的关系,每两个组合的主键决定另外一个主键。比方项目1和经理1就决定了有哪些下属属于项目1和经理1关系,而一个经理1和他的下属员工又决定了他们参与了那些项目。所以他们之间其实是一个循环的依赖。

  ?   反范式:这种类型的应用在商业正常化环境将导致业绩不佳,更精确的数学的必要性比商业要低的多。因此: 

  ?  同样的,对于第4范式,我们很多时候都没有必要去消除他们里面多值的依赖,那样对性能来说简直是个噩梦。所以很多情况下,我们都是用类似的处理CSDN上的,显示自己的技术就是这样的反范式化转换。

  ?  在商业环境中,绝大多数超越第3范式的设计都是不切实际的。因为应用程序在3NF级别就能变现的相当出色。我们上述的很多例子,将指向箭头反过来就是先了反范式化。所以我们要对整体的结构有个比较深的认识,才确定我们是否范式话或者反范式化,范式化越深的东西越导致表的增多,也就意味着查询的join开销。

 

  ?  总结一下反范式化的一些准则:

    ?  分离活动和静态的数据,数据可分为独立的物理表,即

    ?活动和静态表。那些累计的历史数据导致我们占据了绝大多数的空间。这是影响性能的最经常的数据,在数据仓库设计中,我们经常将无效的静态的数据移植到数据仓库中,由于OLAP和数据挖掘。

    ?  在表之间复制字段,在那些不是直接有链接表的之间复制字段,使得我们不必每次进行查询都要通过第3方表,越少的join操作,使得性能的大幅度提升。

    ?  在夫表中建立统计字段,这样可以减去消耗大的聚合操作,但是实时更新会给我们带来另外的麻烦。

    ?  分离繁重和轻松的字段,就像把活动和静态的数据数据表分离一样,这个避免持续物理扫描很少使用的数据字段,尤其是当这些字段不包含空值。这是一个潜在的合理利用4NF在分离表格分为两个表格,相关的一对一关系。

"

上面的文章是有图的, 由于图太多了就没贴, 可以在这里下载有图完整版

最后附上自己记的老师的理解:

2NF:  每个非主属性完全函数依赖于码。(没有非主属性属于2NF)

3NF:  每个非主属性既不部分依赖于码,也不传递依赖于码。

BCNF:每个非主属性和主属性既不部分依赖于码,也不传递依赖于码。(彻底解决删除异常问题)

最后的最后附一张非常简单明了的图:

技术分享

网上的关于多值依赖的解释:

"

什么是多值依赖?书上的概念是:设R(U)是属性集U上的一个关系模式。X,Y,Z是U的子集,并且Z=U-X-Y.关系模式R(U)中多值依赖X→→Y成立,当且仅当对R(U)的任一关系r,给定的一对(x,z)值,有一组Y的值,这组值仅仅决定于x,与z无关!太抽象了!看不懂!

答:举个例子
U{w,s,c} w是主码
x{w}
y{s}
z{c}  可以看到它们满足X,Y,Z是U的子集,并且Z=U-X-Y.
{w,c}->{s},同时s的值和c无关,因为{w,c}->{s},其实是由{w}->{s}得到的。
所以这里就有W->->S
同理可得出W->->C 

"

SQL 范式(转载)