欢迎投稿

今日深度:

写给开发者看的关系型数据库设计(1)(3)

九)规范化——范式

当笔者还在大学时,范式是学习关系型数据库时最头疼的问题。我想也许会有读者仍然不理解范式的价值,简单来说——范式将帮助我们来保证数据的有效性和完整性。规范化的目的如下:

  • 消灭重复数据。
  • 避免编写不必要的,用来使重复数据同步的代码。
  • 保持表的瘦身,以及减从一张表中读取数据时需要进行的读操作数量。
  • 最大化聚集索引的使用,从而可以进行更优化的数据访问和联结。
  • 减少每张表使用的索引数量,因为维护索引的成本很高。

规范化旨在——挑出复杂的实体,从中抽取出简单的实体。这个过程一直持续下去,直到数据库中每个表都只代表一件事物,并且表中每个描述的都是这件事物为止。

1 规范化实体和属性去除冗余)

1NF:每个属性都只应表示一个单一的值,而非多个值。

需要考虑几点:

  • 属性是原子性的 需要考虑熟悉是否分解的足够彻底,使得每个属性都表示一个单一的值。和“三)列意味着唯一的值”描述的原则相同。)分解原则为——当你需要分开处理每个部分时才分解值,并且分解到足够用就行。即使当前不需要彻底分解属性,也应该考虑未来可能的需求变更。)
  • 属性的所有实例必须包含相同数量的值 实体有固定数量的属性表有固定数量的列)。设计实体时,要让每个属性只有固定数量的值与其相关联。
  • 实体中出现的所有实体类型都必须不同

当前设计不符合1NF的“臭味”:

  • 包含分隔符类字符的字符串数据。
  • 名字尾端有数字的属性。
  • 没有定义键或键定义不好的表。

2 属性间的关系去除冗余)

2NF-实体必须符合1NF,每个属性描述的东西都必须针对整个键可以理解为oop中类型属性的内聚性)。

当前设计不符合2NF的“臭味”:

  • 重复的键属性名字前缀设计之外的数据冗余) 表明这些值可能描述了某些额外的实体。
  • 有重复的数据组设计之外的数据冗余) 这标志着属性间有函数依赖型。
  • 没有外键的复合主键 这标志着键中的键值可能标识了多种事物,而不是一种事物。

3NF-实体必须符合2NF,非键属性不能描述其他非键属性。与2NF不同,3NF处理的是非键属性和非键属性之间的关系,而不是和键属性之间的关系。

当前设计不符合3NF的“臭味”:

  • 多个属性有同样的前缀。
  • 重复的数据组。
  • 汇总的数据,所引用的数据在一个完全不同的实体中。有些人倾向于使用视图,我更倾向于使用对象集合,即由程序来完成。)

BCNF-实体满足第一范式,所有属性完全依赖于某个键,如果所有的判定都是一个键,则实体满足BCNF。BCNF简单地扩展了以前的范式,它说的是:一个实体可能有若干个键,所有属性都必须依赖于这些键中的一个,也可以理解为“每个键必须唯一标识实体,每个非键熟悉必须描述实体。”

3 去除实体组合键中的冗余

4NF-实体必须满足BCNF,在一个属性与实体的键之间,多值依赖一条记录在整个表的唯一性由多个值组合起来决定的)不能超过一个。

当前设计不符合4NF的“臭味”:

  • 三元关系实体:实体:实体)。
  • 潜伏的多值属性。如多个手机号。)
  • 临时数据或历史值。需要将历史数据的主体提出,否则将存在大量冗余。)

4 尽量将所有关系分解为二元关系 

5NF-实体必须满足4NF,当分解的信息无损的时候,确保所有关系都被分解为二元关系。

5NF保证在第四范式中存在的任何可以分解为实体的三元关系都被分解。有的三元关系可以在不丢失信息的前提下被分解为二元关系,当分解为两个二元关系的过程要丢失信息时,关系被宣称为处于第四范式中。所以,第五范式建议是,最好把现有的三元关系都分解为3个二元关系。

需要注意的是,规范化的结果可能是更多的表,更复杂的查询。因此,处理到何种程度,取决于性能和数据架构的多方考量。建议规范化到第四范式,原因是5NF的判断太过隐晦。例如:表X老师,学生,课程)是一个三元关系,可以分解为表A老师,学生),表B学生,课程),表C老师,课程)。表X表示某个老师是上某个学生的某个课程的老师;表A表示老师教学生;表B表示学生上课;表C表示老师教课。单独看是无法发现问题的,但是从数据出发,"表X=表A+表B+表C"并不一定成立,即不能通过连接构建分解前的数据。因为可能有多种组合,丧失了表X反馈出的业务规则。这种现象,容易在设计阶段被忽略,但好在在开放阶段会被显现,而且并不经常发生。

推荐做法:

  • 尽可能地遵守上述规范化原则。
  • 所有属性描述的都应该是体现被建模实体的本质的内容。
  • 至少必须有一个键,它唯一地标识和描述了所建实体的本质。
  • 主键要谨慎选择。
  • 在逻辑阶段能做多少规范化就做多少性能不是逻辑阶段考虑的范畴)。

十)选择数据类型MS SQL 2008)

MS SQL的常用类型:

精确数字 不会发生精度损失 bit tinyint smallint int bigint decimal
近似数字 对于极值可能发生精度损失 float(N) real
日期和时间   date time smalldatetime datetime datetime2 datetimeoffset
二进制数据   bingary(N) varbinary(N) varbinary(max)
字符串)数据   char(N) varchar(N) varchar(max) nchar(N) nvarchar(N) nvarchar(max)
存储任意数据   sql_variant
时间戳   timestamp
GUID   uniqueidentifier
XML 不要试图使用该类型规避1NF xml
空间数据   geometry geography
层次数据   heirarchyid

MS SQL中不在支持的或糟糕的类型选择

  • image:被varbinary(max)取代。
  • text和ntext:被varchar(max)和nvarchar(max)取代。
  • money和smallmoney:开发过程中不好用,建议使用decimal。

常用类型选择:

类型选择的最基本规则是选择满足需要的最轻的类型,因为这样查询更快。

bool 建议使用bit而非char(1),因为开发语言对其支持觉好,可以直接映射为bool或bool?。
大值数据 使用所有备选类型中最小的那种,类型越大,查询越慢,当字节大于8000时,应使用max。
主键 自增主键根据预期范围选择int或bigint,GUID使用uniqueidentifier而非varchar(N)。

十一)优化并行

设计DB时就应该考虑到对并行进行优化,比如,MS SQL中的timestamp类型就是极好的选择。

四 命名规则

  • 表——“模块名_表名”。表名最好不要用复数,原因是在使用ORM框架开发时,代码生成器根据DB生成类定义,表生成了某个实例的类型定义,而不是实例集合。表名不要太长。原因之一,某些软件对表名最大长度有限制;原因之二,使用代码生成器往往会根据表名生产类型名称,之后懒人会直接使用这一名称,如果将太长的名称跨网络边界显然不是明智之举。
  • 字段——bool类型用“Is”、“Can”、“Has”等表示;日期类型命名必须包含“Date”;时间类型必须包含“Time”。
  • 存储过程——使用“proc_”前缀。
  • 视图——使用“view_”前缀。
  • 触发器——使用“trig_”前缀。


www.htsjk.Com true http://www.htsjk.com/shujukukf/16993.html NewsArticle 九)规范化范式 当笔者还在大学时,范式是学习关系型数据库时最头疼的问题。我想也许会有读者仍然不理解范式的价值,简单来说范式将帮助我们来保...
评论暂时关闭