数据库设计-闭环
数据库设计中的忌讳
闭环(closed path)
假设现在有一个小公司,其下包含很多项目组,各项目组被安排在不同的房间办公。一个员工只服务于一个项目组。但是,因为房间人数有限,有的项目组成员可能会被安排在不同的房间。我们可能会建立下面的模型:
三个实体之间形成了一个封闭的环。这在很多实际情况下会存在问题。从上面的模型,我们可以得到如下信息一个房间包含多个项目组、一个项目组包含多名员工、一名员工只属于一个项目组、一个员工只能在一个房间、一个房间里有多名员工。我们要求能查出员工所在的项目组,每个房间有哪些员工、有哪些项目组在此房间办公,每个员工的房间的联系电话等等信息。假设我们现在想查询某个员工的房间联系电话,我们可能会直接通过employee和room联结得到,也可能会通过员工所在的项目组找到其所属项目组所在房间的联系电话。数据存在冗余时,当对数据进行更新就会有额外的操作。如果这时调整了员工所在的项目组或是所在项目组挪动了办公地点,这时都要对employee和group进行更新。如果一旦出现错误,则就会出现数据不一致现象。这也应该被称为数据丢失。
那如果现实情况真的是一个项目组被分到了两个房间里,应该如何表示呢?可能你要建立一个虚拟的组,然后分别指示其所在的房间了。当然这并不是一种理想的方法,客户可能并不想这样做。就像不能把所有能自动完成的任务都自动化一样,有时只能取一种折中的办法。
上面模型表示一个员工只服务于一个项目组,若是同时服务于多个项目组时,在employee与room之间建立关系是必要的。模型稍有修改,但同样存在闭环。
这时为了能找到员工所有房间的电话,就只能通过employee和room的关系进行查找了。这无疑给数据的录入和查找带来负面影响,而且数据有丢失的风险。这是无法避免的,但我们如果看到有闭环存在时,一定要谨慎对待了!
假设上面的模型中包含了公司实体,公司下面包含了很多个项目组。有的员工是直属于公司的,服务于所有项目组。我们很可能会建立下面的模型:
我们在employee、company、group之间又看到了封闭的环。你该去掉哪个关系呢?像是works for和belong to都没有办法去掉。这时你就又要考虑虚拟的项目组了,你可以创建一个administrator组来表示服务于所有项目组的员工,他们同样属于公司。从而可以把employee与company之间的关系去掉。
上面的例子你可能很容易想到解决的办法,但当业务比较复杂时,可能很容易就掉进这样的陷阱里。因此,当你看到这样封闭的环时,就要认真的思考一下你对数据是不是真正的理解了,不要为了数据而数据!
数据泛化
假设现在公司为了提高员工技术水平,聘请了一些讲师对员工进行定期的培训,他们每个人负责一部分课程的教授。同时,公司还提供一定的停车位给员工及这些专家免费使用。我们很容易的建立了下面的模型:
同样的问题出现了,你一眼就可以看出,又存在闭环!同时,员工与讲师共享了停车位。如果仔细查看employee与lecturer两个实体,你会发现里面其实有很多属性是相同的。你的模型里属性可能不会像这里一样完全相同,这就要看你是否对每个属性的意义真正理解了!那这些相同的属性我们该如何处理呢?OO强调继承,这里我们也使用继承来解决这个问题。新的模型如下:
这时如果又有新的类型的员工加进来,我们可以再增加一个实体来描述该类型员工所特有的属性了。同时也解决了闭环的问题。
现在又有个问题,有一部分讲师正式加入了公司,也成了公司的员工负责一部分工作。他同时也肩负着培训的工作,拿两份工资。你该如何来描述这种关系呢?这时我们看到变化的是一个人的角色功能,因此你需要添加一个新的实体来表示这种角色,新的模型如下所示:
Contract最后只是一个关联的实体,表示一个人可以和多个角色发生关系。但是,如果在数据库中大量使用继承来表示这种关系的话,会带来很多额外的小表。因此,在实际项目中可能要根据实际情况决定是否要采用这种方式。
表中字段的选择
经常我们在一张表里会看到有很多的字段,而且有很多字段都被设成允许NULL。对于NULL的理解,可以为是、否或不确定。但数据库只适应于处理二值逻辑,这种三值逻辑处理起来会比较麻烦。OO里还特定为了处理数据库中的NULL而单独存在了一种模式。因此,在对字段进行设置时,应尽量的避免允许为NULL。如果有太多的不确定的属性,只能说明你对客户的需求还没有真正了解清楚。大家都知道数据库中的每个数据页只有8K的大小,如果一条记录所包含的字段越少,内容越少。每个数据页所能包含的行数就会越多,因此在进行查询时所发生的I/O就会越小,而I/O正是影响性能最关键的因素。因此你可能需要把一个实体中的属性分别保存于不同的表中。
以上所述,其实归根结底只是强调了一个问题:数据的规范化。最后所建立的物理表中,应该没有冗余的信息。有人会觉得使用冗余来减少表的连接会带来性能的提高,但这在很多情况下是适得其反的。我现在对几个数据库进行过优化,因为不了解业务,所以只能通过改改索引,某些不复杂的SQL语句调整一下写法。但这并不能解决数据库设计的缺陷所带来的影响,因此大家以后在修改数据库这个公共的环境时一定要慎之又慎。虽然现在有很多技术可以支持多个数据库提供服务,但基本都是提供高可用性的,对于高扩展性或多或少都会对程序有一定的影响。因此,在你每写一条SQL语句时,哪怕能提高0.1秒的时间也不要小看。这0.1秒对于一个常用的查询来说,一天所节省的时间是相当可观的!经常在网上看到什么通用的数据库分页存储过程,方便的代价就是性能的损失。因此,对数据库而言,我觉得没有什么是可以通用的。你的查询只能根据表中的数据分布情况来决定如何检索更有效。
更多内容,欢迎大家分享!