對象繼承關係的各種庫表映射對比--《sql反模式》

需求

在業務中, entity實體之間難免出現繼承關係,映射到庫表時如何設計?
繼承體系
下面將介紹這幾種設計方案並對比優缺點:

  1. EAV表(entity-Attribute-Value)
  2. 單表繼承
  3. 實體表繼承
  4. 類表繼承
  5. 半結構化設計
    (技術選型時需要結合具體業務分析再決定方案)

設計方案

EAV表設計

簡介

EAV就是entity-Attribute-Value的意思(實體-屬性-值),非結構化存儲, 看圖表:
user表:

id attr_name attr_value
1 username peter
1 gender
1 birthday 2000-01-01
1 age 19

比如想獲取peter的用戶信息:

select 
	*
 from 
 	user 
 where 
 	id in (select id from user where attr_name = 'peter');

需要查多條記錄到內存再慢慢組裝。

這種存取方式的優缺點:

優勢

  1. 靈活,增刪字段不需要修改庫表結構。不管entity是否存在繼承關係,都可以使用EAV方式存儲。
  2. 庫表設計簡單(id、attr_name、value)

劣勢

  1. 對象的屬性名沒有約束(mysql沒法限定用戶名就叫username,業務可能不小心存爲userName)
  2. 屬性值的類型無法約束(age沒法限定是int類型,業務可能不小心存了個‘abc’進去)
  3. 獲取單個對象數據繁瑣(查詢一個完整對象,需要獲取多條記錄,還不能直接映射爲User對象)
  4. 無法使用完整性檢查特性(沒法使用外鍵)

如果業務在新增屬性時不需要修改庫表結構,可能數據庫採用的就是EAV設計。
(一般業務都建議使用結構化庫表設計,畢竟EAV設計的缺點比較多,但如果業務的屬性頻繁變化,比如埋點事件上報等,可以考慮這種方案。)

單表繼承

簡介

將entity繼承體系的所有屬性都存儲到一個表中,
實體屬性如下:
在這裏插入圖片描述
庫表設計如下:
user表:

id username password age subject student_number
1 teacher1 xxx 40 數學
2 student1 xxx 10 12341234

優勢

  1. 庫表設計簡單
  2. 不需要鏈表查詢即可獲取到子類的完整信息
  3. 規避了EAV設計的很多缺陷

劣勢

  1. 添加子類的屬性時需要修改user表(鎖表,數據量大時影響很大)
  2. 對於子類,表中出現無關的屬性,比如教師的行出現了student_number, 學生的行出現了subject

使用場景
繼承體系中子類屬性較少的情況。比如可預見的時間內子類的屬性都比較少時可以使用這種方式,畢竟查詢簡單,不需要聯表查詢。

實體表繼承

簡介

實體的屬性是完整的,實體表繼承,就是說每個實體都對應一個完整的表。
比如用戶繼承體系中:
在這裏插入圖片描述
那麼庫表中,teacher表擁有完整的屬性。student表也擁有完整屬性,如圖:
庫表設計
這種設計的優缺點:
優勢

  1. 獲取完整對象不需要聯表查詢
  2. 表中沒有無關屬性(跟單表繼承的對比)

劣勢

  1. 在基類添加屬性時需要修改多個表(比如在User類添加birthday屬性,則需要在teacher/student表都添加column)
  2. 表的結構鬆散,看不出類的繼承關係

類表繼承

簡介

類的體系結構如下, 那麼類表繼承的表結構也如下,就是將類的繼承結構映射到庫表上。
類繼承體系
那麼在庫表設計時也有三張表:user表、teacher表、student表(teacher表和student表有外鍵),如下:
庫表繼承體系
優勢

  1. 庫表的層次結構清晰,庫表直接反應了繼承關係
  2. 爲子類添加屬性時不需要修改基類表(user表),爲基類添加屬性時不需要同時爲多個表添加column
  3. 查詢teacher、student的基類信息時不需要查詢多個表(對比實體表繼承方案)

劣勢
4. 獲取對象完整數據需要聯表查詢(在表數據量大時聯表查詢性能差)

半結構化設計

簡介

使用一張表存儲整個繼承體系,但是基類的每個屬性映射到表的對應column,子類的所有屬性則使用一個json/xml類型的column來存儲。舉例:
對於下面的類:
在這裏插入圖片描述
設計庫表如下:
在這裏插入圖片描述
other_properties字段,對於teacher對象來說存{subject=english}, 對於student對象存:{student_number=123}.
那麼在查詢時將整條記錄查詢到內存後再轉爲完整對象。
優勢

  1. 可擴展性強,添加子類屬性時不需要添加column,添加基類屬性時纔會添加column
  2. 查詢簡單,每行對應一個對象的完整信息,不需要聯表查詢

劣勢

  1. 非結構化部分依舊有EAV設計的問題
    1. 對象的屬性名沒有約束(mysql沒法限定用戶名就叫username,業務可能不小心存爲userName)
    2. 屬性值的類型無法約束(age沒法限定是int類型,業務可能不小心存了個‘abc’進去)
    3. 獲取單個對象數據繁瑣(查詢一個完整對象,需要獲取多條記錄,還不能直接映射爲User對象)
    4. 無法使用完整性檢查特性(沒法使用外鍵)
  2. 對於非結構化部分無法使用DB的聚合函數(比如sum、count等)

總結

在這裏插入圖片描述
歡迎點評和點贊…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章