[CS101] 正規化 (Normalization)

我其實對這系列要不要開坑有點猶豫,我知道這一開下去就是不得了的大坑
一路從早上起床就想開工,然後中間睡了午覺還去吃了冰
沒意外還沒打完就要吃晚餐了
這系列主要科普一些資訊背景的人習以為常
但非資訊背景自學程式很容易遺漏的內容
一切的起因都是因為接手的程式太美,資料庫甚至連第一正規畫都沒做

基本上就是我遇到什麼前面人的大便,或是學會有趣的名詞就寫一下

可以把 RDB開得像NoSQL,我也覺得是天才啦
這樣亂開的確可以非常有彈性的解決一直變動的需求
但是這樣在資料庫方面就會增加不必要的負荷
所以這次就來說說資料庫的正規化

先說說為什麼要做資料庫正規化 (Normalization)
正規化是在資料庫中組織資料的程序。
簡單的說就是要將資料的重覆性降至最低,當你要修改一筆資料的內容
不需要在多個地放重複修改

舉例來說:
今天你有多個地方都儲存了學生姓名,如果今天發生學生改名
那你就要找出所有出存有該學生姓名的地方一併修改
若有做了正規化,那只需要改一次!為什麼呢?

打了這麼長先吃個冰休息一下好了,這就是我下午吃的剉冰
點了芋頭、花豆、綠豆跟軟花生,一家冰店的好壞決定在芋頭


吃完冰了來討論一下正規化
一般分為第一正(1NF)規化第二正規化(2NF)....第N正規化
任何正規化必須符合前面所有正規化的條件,才可以成立

第一正規化(1NF)

實務上來說至少要做到1NF,先來看看wiki 定義 第一正規化
一言蔽之的話就是
一個欄位(column)只能有單一值
每一列(row) 必須要具有可供識別的唯一識別碼(主鍵) 

 第二正規化(2NF)

通常我會至少做到第二正規化,這樣遇到例如客戶改名
若只有第一正規化,我每一筆交易都會包含客戶名稱
他一改名我要搜尋所有交易紀錄然後改名,非常的沒效率
並且其實很浪費儲存空間
wiki 的定義是 第二正規化
每一非鍵屬性必須「完全相依」於主鍵
如果我的主鍵是複合主鍵呢?
若是非主鍵的欄位只和部分主鍵有關
則必須獨立出來成為一個資料表
或歸類到該主鍵的資料表
有些人喜歡把流水號 (就是auto-increment產生的index) 刪除
用其他複合鍵作為主鍵,我不喜歡這麼做
因為 int 欄位拿來當 pk 效率比較高,還有把兩個有意義的東西放在一起當PK是很蠢的想法
等到出報表就會生不如死
最重要的是有些框架ORM不支援複合主鍵 XDDD 這蠻嚴重的吧

不符合第二正規化造成最簡單的問題,就是萬一交易紀錄上
我有客戶同名同姓怎麼辦?所以就會加開一張表來記錄客戶資料
並且給定一個唯一主鍵來判斷,例如身分證字號!

第三正規化(3NF)

常常會把第二正規化和第三正規化搞混,2NF就順便做3NF
我們在完成第二正規化,確定所有非主鍵欄位都已經和主鍵有了相依性後
會進行第三正規化
各欄位與主鍵間沒有間接相依的關係
要求各個非鍵值屬性間,只能和鍵值有關係,互相不可具有相關性
有點像是你們這個里的人都相依於這個里,但是你不能和隔壁老王發生關係

例如:你去寄店到店包裹,這筆資料若同時有店舖編號、店鋪地址、店鋪名稱
他們都相依於這筆交易資料,但是地址和店名又和店舖編號有關係
這時候就會把店鋪編號作為另一張表的 index 把店名和地址輸入進去
如此就符合第三正規化

結論:

其實上面這些都是很粗淺的說明而已,還是要實際動手做才會理解
還有一種特殊情況是主鍵是複合鍵值
這種時候還會再進行  Boyce-Codd 正規化 (BCNF)
主要原則就是
主鍵中的各欄位不可以相依於其他非主鍵的欄位。
正規化也不是做得越多越好
我們還要考慮系統的需求自行作一些變化
因為過多的資料表可能會降低系統執行的效能
尤其是當資料量增加到一定程度時,就會明顯感受到系統效能降低

參考資料
http://140.118.9.79/UML-SASD/正規化.pdf
http://au1252.epage.au.edu.tw/ezfiles/294/1294/img/2823/102052570.pdf
http://soon.logdown.com/posts/685164-sql-normalization

留言