逻辑数据库设计 - 可变属性(继承)

  可变属性的需求:咱们须要在数据库里面存储不少电器,好比电视,冰箱等等。一般,在程序中,咱们的类图为:javascript

EVA设计

  对于这种继承下来的可变属性时,有一种办法是建立另一张表,将属性当成行来存储。html

  

  其中存储的数据相似下面这样:java

  

  这样的设计称为:实体-属-值,简称:EVA,或者又叫开放架构、无模式。git

  这种设计有以下3种好处:数据库

  一、这两张表的列都不多。缓存

  二、新增的属性不会对现有的表结构形成影响,不须要新增列。架构

  三、避免因为空值而形成的表内容混乱。数据库设计

  可是这样也有以下缺点:post

  一、查询属性this

  原本,咱们想要按出厂日期查询,只须要:

  SELECT ElectricId,DateOfManufacture FROM Electric

  可是这种方式不行,它须要这样:

  SELECT ElectricId,AttrValue AS 'DateOfManufacture'
  FROM Attribute
  WHERE AttrName = 'DateOfManufacture'

  二、没法声明强制属性

  原本,咱们要确保DateOfManufacture(出厂日期)这个属性有值,在传统数据库设计中,只须要很简单的声明一个NOT NULL就OK了。

  可是如今在EVA设计中,每一个属性对应的是Attribute中的一行。咱们须要创建一个约束来检查对于每一个ElectricId都存在一行,而且这行的AttrName是DateOfManufacture。而且这行记录的AttrValue不为空,而且符合日期格式。

  三、没法使用SQL的数据类型

  因为AttrValue的格式只能声明为Varchar或NVarchar类型,所以用户输入的日期格式多是各类各样,甚至有的根本就不是日期格式。

  因为数据类型不可以由限制,所以咱们执行以下SQL语句也不会报错。

  INSERT Attribute VALUES(1,'DateOfManufacture','我不是一个日期')  --这样的语句也不报错

  四、没法确保引用完整性

  加入上面的设计,咱们须要添加一个品牌属性。可选值必须是存在的好比,三星,康佳,海尔等等。在传统的数据库设计中,咱们只须要设计一张品牌表,并给本表添加一个品牌Id字段,创建外键约束就能够了。

  可是,在EVA设计中,由于品牌属性对应的是一行,所以咱们没法使用外键来确保引用完整性。若是咱们不处理,那么用户输入的品牌属性的值多是不存在的。

  五、重复记录

  在EVA设计中,咱们可能将同一个属性了两次。

  由于,咱们连续执行以下SQL语句两次也是不报错的:

  INSERT Attribute VALUES(1,'DateOfManufacture','2013-09-09')
  INSERT Attribute VALUES(1,'DateOfManufacture','2013-09-10')

  因为可能存在重复记录,所以咱们按出厂日期统计出厂产品数量也并不可靠。同时,按日期统计,也很复杂。

  SELECT ElcDate, COUNT(*) AS Per_Date 
  FROM (SELECT DISTINCT ElectricId,AttrValue AS ElcDate
        FROM Attribute
        WHERE AttrName = 'DateOfManufacture')
  GROUP BY ElcDate

  这是Oracle中的写法。

  六、重组列

  在传统数据库设计中,加入咱们要显示一条完整的记录,咱们只须要:

  SELECT * FROM Electric

  可是如今,咱们要:

复制代码
  SELECT i.ElectricId,
    i1.AttrValue AS 'Name',
    i2.AttrValue AS 'DateOfManufacture',
    i3.AttrValue AS 'Screen'
  FROM Electric AS i
    LEFT OUTER JOIN Attribute AS i1 ON i.ElectricId = i1.ElectricId AND i1.AttrName='Name'
    LEFT OUTER JOIN Attribute AS i2 ON i.ElectricId = i2.ElectricId AND i2.AttrName='DateOfManufacture'
    LEFT OUTER JOIN Attribute AS i3 ON i.ElectricId = i3.ElectricId AND i3.AttrName='Screen'
复制代码

  不在多说,总而言之,以上的设计,并不是一个很是耐得住推敲的设计。

解决方案

  1、单表继承

  单表继承的设计是将全部相关的类型都存在一张表中,为全部类型的全部属性都保留一列。同时使用一个属性来定义每一行表示的子类型。

  例如,对于以上电器的需求,单表继承的数据设计以下:

  

  单表继承的方式能够理解为,全部子类的字段,都往单表里放,存储的时候,当某子实体没有的时候,相应的类为空,都是预留一列做为标记类型。

  单表继承的缺点就是:

  •   列过多。
  •   过多NULL值。
  •   当要增长属性的时候,要改动表结构。

  综上所述:单表继承只是适合使用子类的特殊属性列很少的状况。

  2、实体表继承

  实体表继承能够理解为:子表在设计的时候,将父表的全部的属性所有都在本表定义多一次。

  回到上面的例子,若是用实体表继承的话,对应的设计以下:

  

  实体表继承相比于单表继承,有一个好处,就是防止在一行内存储太多和当前子类型无关的属性。好比在冰箱表里没有了屏幕列,而在单表继承中,是由Scree列的NULL值的。另外,也不用在加多一个列用于标记当前是什么电器。

  实体表继承的致命缺点:

  重复列过多

  重复列过多,很容易让人摸不着头脑。

  3、类表继承

  个人推荐,我最喜欢,我认为最可靠的方式

  类表继承模拟了高级程序语言中的继承,把表当成面向对象里的类。建立一张基表,包含全部子类型的公共属性。对于每一个子类型,建立一个独立的表,经过外键和基类表相连。

  对以以上例子,类表继承的设计以下:

  

  类表继承,相比于实体类继承,明显的有点在于,少了不少重复列。子类表中,主键同时也是外键。

  我认为这是一个比较好的方法。

  4、半结构化

  半结构化,实际上跟单表继承差很少。单表继承是多个列,而半结构化使用一个新特性,好比一个xml类型的列,来存储子类的属性。

  对于以上例子,半结构化的设计以下:

  

  子类的信息,存在一个XML列中,你爱设置什么节点就什么节点。反正查询起来也不麻烦。不够要记住的是,要有一个Type列,来标记哪行是哪一种电器。否则就全乱套了。

  因为,如今SQLServer对XML的支持愈来愈强大,这也是一个不错的选择。

 
 
 
2
0
 
(请您对文章作出评价)
 
« 上一篇: NHibernate使用MemCache二级缓存
» 下一篇: 逻辑数据库设计 - 多态关联
相关文章
相关标签/搜索