iT邦幫忙

1

是否要資料庫正規化

deh 2021-02-20 13:57:332214 瀏覽

各位前輩好,想請教是否要資料庫正規化的問題
目前用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會比較好?

看更多先前的討論...收起先前的討論...
ckp6250 iT邦研究生 1 級 ‧ 2021-02-20 14:39:12 檢舉
>故正規化後TableB應該去除年月的欄位

能省則省。
Homura iT邦高手 1 級 ‧ 2021-02-20 16:00:46 檢舉
中文欄位...
年月怎不用datetime...
deh iT邦新手 4 級 ‧ 2021-02-20 16:25:05 檢舉
欄位是
'select col as 年月'
出來的 想說用中文表示比較清楚
PPTaiwan iT邦新手 3 級 ‧ 2021-02-20 18:39:00 檢舉
不知道你要的方法是什麼,基本上 TableA,TableB 可以合成一個 Table 就可以了,可以先不用 GUID。

我認為正規化前應該先 "以需求可以先以你的想法先行設計" ,再從中去理解與重構你的 Table 才能夠學習到 "正規化的精神" 。
D路里 iT邦新手 5 級 ‧ 2021-02-21 16:51:25 檢舉
小系統小資料庫
好用 方便用優先
deh iT邦新手 4 級 ‧ 2021-02-22 08:41:14 檢舉
謝謝樓上兩位大大指導,我再想想怎樣做比較合適
如果資料是一對一的關係,說真的不必要分成兩個甚至更多個table,只會讓你在提資料時join幾個table進來,不見得是"正規",但如果是一對多的關係,甚至多到無限量筆數的對應,才會有關聯表的必要,何謂正規化,一個一開始設計,就是可被估計且足夠用的資料環境,而不是用不到一兩年就開始欄位不夠狂加欄位,TABLE不夠瘋開TABLE....程式改不完,報表修不完
2
pojen
iT邦新手 1 級 ‧ 2021-02-21 00:06:59
最佳解答

唔... 這跟豆花要甜的還是鹹的問題是一樣的. 我只能說資料庫是拿來用的, 舉例來說, 如果我們為了正規化而去掉一些所謂"重複性"的欄位, 那你使用時如果需要這個欄位, 那你必須 join 兩個表格, 那效能可想而知. (當然 MSSQL 有 schemabinding view, 與 Oracle 的 materalized view 可以把結果寫到另一個表格上, 但他需要儲存空間與多出來的 DML 來同步)

在資料庫的世界沒有白吃的午餐, 如果有的話他大概已經是需要付費的功能(萬惡的授權)

不過根據你的表格範例, 我至少可以告訴你我不喜歡 UUID. 如果你需要 JOIN 兩個表格, 大概率你會有 index 在 UUID 上, 而 UUID 很容易造成 index leaf split. (中文不能...) 你可以說 UUID 可以減少 hot block 發生概率. 但就像我說的豆花(或是粽子)要甜的還是鹹的都可以.

另: 我是豆花甜黨, 粽子鹹黨. 不接受反駁.

deh iT邦新手 4 級 ‧ 2021-02-22 08:33:22 檢舉

謝謝大大指導,之前把正規化當SOP,才有疑問

jdiii iT邦新手 5 級 ‧ 2021-02-24 15:41:26 檢舉

可是鹹豆漿很好喝耶XD

2
海綿寶寶
iT邦大神 1 級 ‧ 2021-02-20 14:56:51

TableB的年月跟TableA的年月是相同的

就衝著這句話
我投「把 TableA 刪掉」一票
/images/emoticon/emoticon59.gif

deh iT邦新手 4 級 ‧ 2021-02-20 15:00:39 檢舉

窘,TableA內還有其他欄位啦~只是這裡沒有select出來。A跟B的關係有點像是箱子跟內容物,箱子有箱子的條碼之類的

deh iT邦新手 4 級 ‧ 2021-02-20 15:02:02 檢舉

我更新一下圖片好了 哈哈

TableA內還有其他欄位啦~只是這裡沒有select出來

GIGO
/images/emoticon/emoticon10.gif

1
oscarhsu
iT邦見習生 ‧ 2021-02-21 19:23:36

同pojen的看法(但經驗沒他豐富)。

以前學資料庫系統時,就有注意到正規化雖然把儲存空間有效利用,但實際使用時要join,造成CPU cost增加。
CPU資源比較珍貴,硬碟可以多用些;所以不要正規化過頭比較好。
(前提,你重覆的資料欄一定同步,不會有錯)

deh iT邦新手 4 級 ‧ 2021-02-22 08:35:33 檢舉

謝謝大大指導,該有所取捨,以前都以為正規化就比較好

2
I code so I am
iT邦研究生 2 級 ‧ 2021-02-21 22:54:47

依資料庫理論,設計應先正規化,再依商業需求作反正規化(De-normalization),看這兩個表格好像工作或交易記錄表,那就不需要考慮正規化了,因為,這些表只是要記錄過去發生的事實,不需要與主表連結。

deh iT邦新手 4 級 ‧ 2021-02-22 08:38:03 檢舉

謝謝大大指導,之前都沒做到反正規化,目前這表想想確實是不繼續正規化好些

1
bizpro
iT邦大師 1 級 ‧ 2021-02-23 16:13:03

資料庫正規化, Database Normalization, 是商業邏輯分析的果, 不是資料結構設計的因. 通常做不到資料庫正規化3NF的原因可能是商業邏輯有漏洞: 如果您在資料庫正規化設計上猶豫, 表示您的商業邏輯不穩. 沒有反正規化這東西的, 應該是對資料庫正規化的誤解, 而您的案例並未違反3NF正規化:
1NF: 一個蘿蔔一個坑, 不要擠在一起.
2NF: 不拖泥帶水, 老是帶一些自己的東西來.
3NF: 別攀親帶故, 賴著間接關係就要來.
同時有年月,和資料庫正規化1~3NF's有什麼關係? 年月拖了誰的泥帶了誰的水? 攀了誰的親又帶了誰的故? 充其量就是喜歡到處有年月.

資料庫正規化是檢驗商業邏輯的方法, 做不到資料庫正規化3NF, 表示商業邏輯不穩, 不穩的商業邏輯, 跑久了就很累, 不耐操, 終究會出錯的, 出來混, 總是要還的.

關於UUID用在關聯式資料庫的叢集索引, 吹皺一池春水(index leaf split), 為什麼要這麼做? 為了資料庫叢集? 為了資料備份? 用不著要用UUID來處理這種問題, 搬石頭砸自己的腳罷了, 對UUID索引又有什麼意義? 還是誤解了索引的意義?

deh iT邦新手 4 級 ‧ 2021-02-25 08:53:03 檢舉

TableB的年月,來源是TableA_Guid的年月,所以違反2NF。
用UUID當index是為了1NF,當然流水號也能做到,剛接觸DB的時候有了解一下(翻翻網路上的討論),後來才選擇用UUID的,可能了解的不夠,要再加強些
index leaf split搜尋後貌似是Oracle特有的東西,確實不知道那是什麼

bizpro iT邦大師 1 級 ‧ 2021-02-25 14:46:16 檢舉

'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做為叢集索引.也保持索引愈簡單愈好.

deh iT邦新手 4 級 ‧ 2021-02-25 16:36:49 檢舉

謝謝大大的詳細解說。
以前用過以資料庫生成流水號當PK的,後來看別人用UUID唯一值當PK感覺更好用。當時爬文也看到各DB都有對UUID當PK特別處裡,認為除了多佔點空間以外影響不大,之後就都用UUID當PK了......DB方面還得加強加強。
再次感謝。

我要發表回答

立即登入回答