如何實現/建構可以處理 具有許多可選屬性的類型/表的 SQL 數據庫模式?
我正在研究一個精簡的數據庫模式(在 PostgreSQL 中),比如舊的FreeBase,只是沒有那麼多東西。到目前為止大約有 100 張桌子,但那是在我考慮為特殊情況添加幾十張之前,感覺不太對勁。讓我用一個簡單的例子來解釋這個問題,它以一種小的方式複制了這個問題,想像一下它會變得更加複雜,一個表有幾十個可選的屬性/關係/關聯,並且有許多不同的相互連接的表,像這樣。
我知道並廣泛使用過像 MongoDB 這樣的 NoSQL 文件數據庫,並且在像 Neo4j 這樣的圖形數據庫中涉足過。由於這個副項目的複雜性,它們是我寧願避免使用的工具,而且用於部署的工具和資源與當今世界的 PostgreSQL 之類的東西不一樣。
因此,為了說明這個問題,想像一個“符號”表,其中包含所有unicode符號,以及 unicode 範圍之外的數千個符號(徵兵、瑪雅腳本、其他非腳本符號等)。基表如下所示:
table symbols { id unicode (optional) preview_image_url (optional) title description }
我們已經有了一些可選屬性,因為有些符號沒有 unicode,有些不需要預覽圖像(所有 unicode 都可以在瀏覽器中呈現,等等)。但是,讓我們考慮一下我們想要儲存有關…的結構化資訊的其他一些“類型”符號。
首先,我們可以想到“腳本”符號,即用於書寫系統的符號。很酷,我們可以在我們的表中添加可選的“script_name”屬性,這還不錯。但是不,什麼樣的腳本符號?有從右到左的文字、垂直文字、表意文字、字母文字、abjads 和 abugidas 等。一些字母文字如拉丁文字有鏡像符號(如括號)或大寫/小寫對。一些腳本將字元與特定規則組合在一起,這些規則可以與哪些組合。有些符號純粹是裝飾性的,有些是幾何的。因此,我們嘗試考慮所有這些可選功能:
table symbols { id unicode (optional) preview_image_url (optional) title description is_logographic (optional) is_vertical (optional) is_rtl (optional) is_alphabet (optional) is_abjad (optional) is_abugida (optional) script_name (optional) mirror_image_symbol_id (optional) uppercase_symbol_id (optional) lowercase_symbol_id (optional) combining_class (optional) }
不過,有些人可能會說擁有所有這些可選屬性還不錯,我不知道。
然後您可以繼續並添加更多子子類型….
- 類似三角形的符號(在 unicode 中有一些)
- 陰影三角形符號
- 空三角形符號
想像一下你可以嘗試在Google上搜尋與符號相關的所有可能的東西。
看起來像“c”的符號
- ©(版權符號)
- 🄯(版權符號)
- ℃(攝氏度的符號)
- ¢(美元的美分符號)
- ₡(哥斯達黎加和薩爾瓦多貨幣冒號的符號)
- ₵(塞地符號,加納貨幣)
- ₢(克魯塞羅的符號,巴西的歷史貨幣)
- ℄(實際上是“cl”
帶有內置組合標記的符號,如 é。
1 字節的 Unicode 字形
2 字節 unicode 字形
4字節…
…
它開始變成這樣:
table symbols { id unicode (optional) preview_image_url (optional) title description is_logographic (optional) is_vertical (optional) is_rtl (optional) is_alphabet (optional) is_abjad (optional) is_abugida (optional) script_name (optional) mirror_image_symbol_id (optional) uppercase_symbol_id (optional) lowercase_symbol_id (optional) combining_class (optional) is_triangle_like (optional) is_shaded_triangle_like (optional) is_empty_triangle_like (optional) looks_like_c (optional) looks_like_d (optional) looks_like_l (optional) ... has_built_in_diacritic (optional) is_1_byte (optional) is_2_bytes (optional) ... }
很快我們就會得到 50 或 100 個可選欄位。當您嘗試對“生物體”及其所有獨特而多樣的特徵進行建模時,您可以想像這會變得更加複雜!數以千計的可選功能,並且沒有明確的 OO 類層次結構來創建子類,它更像是一個互連組合的圖/網路。
所以我的想法開始轉向讓事情變得超級抽象/通用,並創建一個名為“facts”的表格,比如:
table facts { id object_type object_id property_name value_type value_id }
這樣你就可以創建一個像“symbol a”這樣的對象,並在它上面有“facts”,比如“property name is
script_name
and value type is a strings table with a string mapped to an ID, as an property on the symbol type of object” . 或者另一個事實是:// facts table id: 123 object_type: 'symbol' object_id: 12321 property_name: 'is_1_byte' value_type: 'boolean' value_id: 444 // boolean table id: 444 value: true // symbol table id: 12321 unicode: 'a'
但是沿著這條路走下去,你最終只會得到幾個表(基本上是“事實”表,可能還有 1 或 2 個其他元表),而不是 100 個。但是事情變得更加難以思考和視覺化,並且查詢變得更複雜一些。
但我看不到解決這個問題的方法。我傾向於讓數據庫成為這種抽像類型的“事實”表,但是在應用程序層中讓它看起來更物件導向,就像在 JavaScript 中一樣,它有屬性或沒有屬性。我想稍微“強化”一下,並給每個組合/變體一個不同的類型名稱,但這並不完全奏效,例如:
{ type: 'alphabet_symbol', value: 'e' }, { type: 'geometric_symbol', value: '▲' }
然後建構一個類型樹:
symbol alphabet_symbol mirror_image_alphabet_symbol mirror_image_alphabet_symbol_with_capital_lowercase capital_lowercase_alphabet_symbol abjad_symbol geometric_symbol triangle_geometric_symbol shaded_triangle_geometric_symbol
但這打破了:
symbol alphabet_symbol (cyrillic б) look_like_6_symbol (cyrillic б)
所以就像,也許只是向中心對象添加標籤。
б id: 455 tags - name: 'is_alphabet_symbol' symbol_id: 455 - name: 'looks_like_6_symbol' symbol_id: 455
但在這一點上,它歸結為我最初在您開始嘗試處理更多案件時分享的通用/抽象“事實”想法。
// facts table id: 124 object_type: 'symbol' object_id: 455 property_name: 'is_alphabet_symbol' value_type: 'boolean' value_id: 444 // boolean table id: 444 value: true // symbol table id: 455 unicode: 'б'
所以想知道,這裡簡要概述的處理“類型”的動態性和變化的推薦方法是什麼?您如何平衡擷取盡可能多的結構化數據的願望而不製作一個大的可選填充平面表(這似乎在幾十個可選列之後分解,更不用說在有機體建模的情況下是 100 或 1000) .
抽像數據庫層是一個很常見的想法,但這樣做會失去數據庫系統的很多關係方面(令人驚訝的是,這在關係數據庫管理系統中並不是一個好主意)。這被稱為EAV 反模式,通常應該避免有幾個原因,其中一些包括查詢的複雜性增加,正如您所注意到的,還有一些是性能問題。
在您的數據對像上擁有可為空的屬性並不是世界末日,即使這會導致很多列。在 ERP 系統的數據庫中看到這種情況並不少見,這些數據庫通常有數千到數十萬個表,有些每個表有幾百到數千列。但這也不是最好的設計。
正如大衛所說,更好的解決方案是規範化您的數據對象。考慮它的一個好方法是重構除對象表示的每個(或大多數)記錄共有的核心屬性之外的任何內容。將其他相關屬性分組到自己的表中。這確實可能會導致更多的表,但如前所述,許多表在功能上沒有任何問題。當然,維護工作量更大,但這是對適當設計的關係模式的權衡,有利於提高性能(否則本身可能需要大量工作)、改進的相關性以及改進的數據管理和準確性。