Database-Design

這個鍵值數據庫模式有名稱嗎?

  • May 24, 2021

我們處理來自客戶的例行數據饋送,該客戶剛剛將其數據庫從看起來熟悉的形式(每個實體一行,每個屬性一列)重構為我不熟悉的形式(每個實體每個屬性一行):

之前:每個屬性一列

ID   Ht_cm   wt_kg   Age_yr  ... 
1      190      82     43    ...
2      170      60     22    ...
3      205      90     51    ...

之後:所有屬性的一列

ID    Metric   Value
1     Ht_cm     190
1     Wt_kg     82
1     Age_yr    43
1      ...
2     Ht_cm     170
2     Wt_kg     60
2     Age_yr    22
2     ...
3     Ht_cm     205
3     Wt_kg     90
3     Age_yr    51
3     ...

這個數據庫結構有名字嗎?相對優勢是什麼?舊方法似乎更容易對特定屬性(非空、非負等)設置有效性約束,並且更容易計算平均值。但是我可以看到在不重構數據庫的情況下添加新屬性可能會更容易。這是結構化數據的標準/首選方式嗎?

它被稱為實體-屬性-值(有時也稱為“名稱-值對”),當人們在關係數據庫中使用 EAV 模式時,它是“方孔中的圓釘”的經典案例。

以下是您不應該使用 EAV 的原因列表:

  • 您不能使用數據類型。值是日期、數字還是貨幣(十進制)都沒有關係。它總是會被強制轉換為 varchar。這可能是從輕微的性能問題到嚴重的腸痛(曾經不得不在每月匯總報告中追踪一美分的變化?)。
  • 您不能(輕鬆)強制執行約束。它需要大量的程式碼來強制執行“每個人的身高都必須在 0 到 3 米之間”或“年齡不得為空且 >= 0”,而不是每個約束都需要 1-2 行在適當建模的系統中。
  • 與上述相關,您不能輕易保證您獲得每個客戶所需的資訊(其中一個可能缺少年齡,然後下一個可能缺少他們的身高等)。你可以做到,但它比SELECT height, weight, age FROM Client where height is null or weight is null.
  • 再次相關,重複數據更難檢測(如果他們給你一個客戶的兩個年齡會發生什麼?如下所示,如果你有一個屬性加倍,則對數據進行去 EAV 會給你兩行結果。如果一個客戶對於兩個屬性有兩個單獨的條目,您將從下面的查詢中獲得四行)
  • 您甚至不能保證屬性名稱是一致的。“Age_yr”可能會變成“AGE_IN_YEARS”或“age”。(誠然,當您收到數據提取與人們插入數據時相比,這不是問題,但仍然如此。)
  • 任何類型的非平凡查詢都是一場徹底的災難。要將三屬性 EAV 系統關係化,以便您可以以合理的方式查詢它,需要 EAV 表的三個連接。

比較:

SELECT cID.ID AS [ID], cH.Value AS [Height], cW.Value AS [Weight], cA.Value AS [Age]
FROM (SELECT DISTINCT ID FROM Client) cID 
     LEFT OUTER JOIN 
   Client cW ON cID.ID = cW.ID AND cW.Metric = "Wt_kg" 
     LEFT OUTER JOIN 
   Client cH ON cID.ID = cH.ID AND cW.Metric = "Ht_cm" 
     LEFT OUTER JOIN 
   Client cA ON cID.ID = cA.ID AND cW.Metric = "Age_yr"

到:

SELECT c.ID, c.Ht_cm, c.Wt_kg, c.Age_yr
FROM Client c

這是您應該使用 EAV 的時間的(非常短的)列表:

  • 絕對沒有辦法解決它並且您必須在數據庫中支持無模式數據時。
  • 當您只需要儲存“東西”並且不希望以更結構化的形式需要它時。不過要小心,這個怪物叫做“不斷變化的要求”。

我知道我剛剛花了整篇文章詳細說明了為什麼在大多數情況下 EAV 是一個糟糕的主意 - 但在少數情況下它是必要的/不可避免的。然而,大多數時候(包括上面的例子),它會比它的價值要麻煩得多。如果您需要廣泛支持 EAV 類型的數據輸入,您應該考慮將它們儲存在鍵值系統中,例如 Hadoop/HBase、CouchDB、MongoDB、Cassandra、BerkeleyDB。

引用自:https://dba.stackexchange.com/questions/20759