各位前輩好,想請教是否要資料庫正規化的問題
目前用MS SQL 如下圖有兩個Table
TableA
TableB
TableA_Guid對應多個TableB_Guid
TableB的年月跟TableA的年月是相同的
故正規化後TableB應該去除年月的欄位
但有TableB有年月時,TableA對應到TableB可以多一個where條件
是否不正規化會比較好?
用SQL來說的話,正規化前可以將下面兩個Table Join得出目標
select TableA_Guid from TableA where 年月 >= 202010 AND 年月 <= 202011 --約1K筆資料(1K = 1000)
select TableB_Guid,TableA_Guid from TableB where 年月 >= 202010 AND 年月 <= 202011 --約10K筆
但正規化後變成
select TableA_Guid from TableA where 年月 >= 202010 AND 年月 <= 202011 --約1K筆資料
select TableB_Guid,TableA_Guid from TableB --N筆
是否不正規化會比較好?
或者說靠index/FK,正規化後也不會有多少影響?
或者弄一個View,程式再去那個View用where會比較好?
唔... 這跟豆花要甜的還是鹹的問題是一樣的. 我只能說資料庫是拿來用的, 舉例來說, 如果我們為了正規化而去掉一些所謂"重複性"的欄位, 那你使用時如果需要這個欄位, 那你必須 join 兩個表格, 那效能可想而知. (當然 MSSQL 有 schemabinding view, 與 Oracle 的 materalized view 可以把結果寫到另一個表格上, 但他需要儲存空間與多出來的 DML 來同步)
在資料庫的世界沒有白吃的午餐, 如果有的話他大概已經是需要付費的功能(萬惡的授權)
不過根據你的表格範例, 我至少可以告訴你我不喜歡 UUID. 如果你需要 JOIN 兩個表格, 大概率你會有 index 在 UUID 上, 而 UUID 很容易造成 index leaf split. (中文不能...) 你可以說 UUID 可以減少 hot block 發生概率. 但就像我說的豆花(或是粽子)要甜的還是鹹的都可以.
另: 我是豆花甜黨, 粽子鹹黨. 不接受反駁.
TableB的年月跟TableA的年月是相同的
就衝著這句話
我投「把 TableA 刪掉」一票
同pojen的看法(但經驗沒他豐富)。
以前學資料庫系統時,就有注意到正規化雖然把儲存空間有效利用,但實際使用時要join,造成CPU cost增加。
CPU資源比較珍貴,硬碟可以多用些;所以不要正規化過頭比較好。
(前提,你重覆的資料欄一定同步,不會有錯)
依資料庫理論,設計應先正規化,再依商業需求作反正規化(De-normalization),看這兩個表格好像工作或交易記錄表,那就不需要考慮正規化了,因為,這些表只是要記錄過去發生的事實,不需要與主表連結。
資料庫正規化, Database Normalization, 是商業邏輯分析的果, 不是資料結構設計的因. 通常做不到資料庫正規化3NF的原因可能是商業邏輯有漏洞: 如果您在資料庫正規化設計上猶豫, 表示您的商業邏輯不穩. 沒有反正規化這東西的, 應該是對資料庫正規化的誤解, 而您的案例並未違反3NF正規化:
1NF: 一個蘿蔔一個坑, 不要擠在一起.
2NF: 不拖泥帶水, 老是帶一些自己的東西來.
3NF: 別攀親帶故, 賴著間接關係就要來.
同時有年月,和資料庫正規化1~3NF's有什麼關係? 年月拖了誰的泥帶了誰的水? 攀了誰的親又帶了誰的故? 充其量就是喜歡到處有年月.
資料庫正規化是檢驗商業邏輯的方法, 做不到資料庫正規化3NF, 表示商業邏輯不穩, 不穩的商業邏輯, 跑久了就很累, 不耐操, 終究會出錯的, 出來混, 總是要還的.
關於UUID用在關聯式資料庫的叢集索引, 吹皺一池春水(index leaf split), 為什麼要這麼做? 為了資料庫叢集? 為了資料備份? 用不著要用UUID來處理這種問題, 搬石頭砸自己的腳罷了, 對UUID索引又有什麼意義? 還是誤解了索引的意義?
TableB的年月,來源是TableA_Guid的年月,所以違反2NF。
用UUID當index是為了1NF,當然流水號也能做到,剛接觸DB的時候有了解一下(翻翻網路上的討論),後來才選擇用UUID的,可能了解的不夠,要再加強些
index leaf split搜尋後貌似是Oracle特有的東西,確實不知道那是什麼
'TableB的年月,來源是TableA_Guid的年月,所以違反2NF。
這是錯誤的商業邏輯. 年月只是時間的紀錄, 單純業務邏輯的判斷, 不是來自那一個Table. 年月是自己一個來的, 不是嗎? 是誰拖泥帶水了年月來嗎? 怎違反了2NF? 沒有反正規化這種事的, 如果不能正規化, 表示商業邏輯有漏洞.
技術上, 叢集索引是把資料和索引放在index leaf中, b-tree叢集索引樹的樹葉(leaf), UUID的順序是無法預期的, 就會使b-tree到處跳躍分裂(split), 天女散花, 也就是實際資料儲存破碎化, 還有, UUID至少32位元, 資料量愈大, 叢集索引負擔愈重, 為此, Oracle, PostgreSQL新版, MariaDB新版,SQL Server...等都有特別處理UUID當叢集索引,例如在資料庫內部使用分離索引(非叢集)的方式處理UUID作為叢集索引, 明修棧道暗渡陳倉, 這就限制了資料庫的移植性.
沒有說不能用UUID做叢集索引(通常是主鍵值), 當然, 您有權力拿石頭砸自己的腳, 但是除了腳會痛外, 商業邏輯的意義何在? 而且如果您要告訴其他人說有一筆資料的主鍵值是長長的UUID, 一下大寫一下小寫, 數字1還是英文E? 數字0還是英文O? 數字1還是英文l? 多困擾啊.
如果為了備份, 負載平衡, 叢集,...而使用UUID, 其實也沒必要, 難道每一個資料表的主鍵值都是UUID? 序列號主鍵值已經夠用了, 對序列號主鍵值而言, 資料庫本身也有區分主機的機制進行規律間隔跳號, 也可以使用客製化索引, 在程式碼控制外, 有人喜歡使用資料庫的內建功能, 或使用Insert Trigger, 但那會影響資料庫移植性, 我寫了一個取號機, 用資料表取號(也計畫用Redis取號), 對叢集索引友善, 每個資料表的主鍵值跨資料庫都不會有機會重複的.取號機的資料結構很簡單: idprefix, idsequence, idlock三個欄位. idlock是用來鎖定取號的, 用程式碼處理鎖定.
我在一個案子中使用UUID當JSON的"主鍵值", 但那不是叢集索引. 在取號機中, 我也用UUID進行取號鎖定. UUID好用, 但不是一定要用.
叢集索引通常是主鍵值, 如果您用UUID做主鍵值, 不要用UUID做為叢集索引.也保持索引愈簡單愈好.
謝謝大大的詳細解說。
以前用過以資料庫生成流水號當PK的,後來看別人用UUID唯一值當PK感覺更好用。當時爬文也看到各DB都有對UUID當PK特別處裡,認為除了多佔點空間以外影響不大,之後就都用UUID當PK了......DB方面還得加強加強。
再次感謝。