邏輯數據庫設計 - 可變屬性(繼承)

  可變屬性的需求:咱們須要在數據庫裏面存儲不少電器,好比電視,冰箱等等。一般,在程序中,咱們的類圖爲: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二級緩存
» 下一篇: 邏輯數據庫設計 - 多態關聯
相關文章
相關標籤/搜索