Logo

新人日誌

首頁關於我部落格

新人日誌

Logo

網站會不定期發佈技術筆記、職場心得相關的內容,歡迎關注本站!

網站
首頁關於我部落格
部落格
分類系列文

© 新人日誌. All rights reserved. 2020-present.

資料庫架構設計入門:用第三正規化刪除多餘欄位

最後更新:2026年1月7日資料庫

上一篇文章我們學到了第二正規化形式:把相依的欄位放在同一張表單,不相依的欄位拆到不同的表單。

例如「會員」和「會員信箱」是相依的(知道會員就知道信箱),所以放在同一張會員表單。

但有時候,表單裡還會有另一種「多餘」的欄位——它不是直接重複,而是可以計算出來的。

這篇文章會介紹第三正規化形式,教你怎麼找出這些多餘的欄位,並且把它們刪掉。

第二正規化之後還有哪些多餘欄位

在第二正規化形式中,我們把「會員信箱」拆到會員表單,把「飯店電話」拆到飯店表單。

這些欄位都不是多餘的——你刪掉「會員信箱」,就真的找不到會員的信箱了;你刪掉「飯店電話」,就真的找不到飯店的電話了。

但是,有些欄位就不一樣了。

總價是多餘的嗎?

看一下我們目前的表單:

房型表單:

房型 ID房型名稱單價飯店 ID
R001雙人房3000H001
R002總統套房10000H001
R003單人房2000H002
房型名稱雙人房
單價3000
飯店 IDH001
房型名稱總統套房
單價10000
飯店 IDH001
房型名稱單人房
單價2000
飯店 IDH002

訂房表單:

會員 ID房型 ID入住天數總價
U001R00126000
U001R00336000
U002R002110000
房型 IDR001
入住天數2
總價6000
房型 IDR003
入住天數3
總價6000
房型 IDR002
入住天數1
總價10000

你有沒有發現「總價」這個欄位怪怪的?

以第一筆資料為例:房型 ID 是 R001,從房型表單可以查到單價是 3000。然後 3000 × 2 天 = 6000,就是總價。

換句話說,總價是可以計算出來的,不需要另外存。

可推算欄位的問題

如果你把「總價」存在表單裡,會有什麼問題?

房型漲價要改總價

假設台北大飯店的雙人房要漲價,從 3000 改成 3500。

你需要做什麼?

  1. 去房型表單改「單價」→ 3500
  2. 去訂房表單改所有雙人房的「總價」→ 重新計算

如果這個房型已經被訂了 100 次,你就要重新計算 100 筆「總價」。

改天數也要改總價

假設會員想把入住天數從 3 天改成 2 天。

你需要做什麼?

  1. 改「入住天數」→ 2
  2. 改「總價」→ 重新計算

這樣每次修改,都要記得去改兩個地方。如果漏改了,資料就會不一致。

可推算的值也是一種重複

你可能會想:「總價」跟「單價」的值又不一樣,怎麼會是重複?

我們換個方式想:

  • 如果你知道「單價」和「入住天數」,你能不能知道「總價」?→ 可以,算一下就知道了
  • 如果你不知道「單價」和「入住天數」,光看「總價」有意義嗎?→ 有,但這個資訊其實是從前兩者來的

所以「總價」並沒有提供新的資訊,它只是把「單價 × 入住天數」的結果存起來而已。

把這種「可以算出來的值」另外存一份,就是一種「重複」——不是值的重複,而是資訊的重複。

第三正規化形式

第三正規化形式告訴我們:

如果一個欄位的值可以計算出來,就把它刪掉。

為什麼?因為這個欄位:

  • 沒有提供新的資訊(它的值可以算出來)
  • 會造成維護的負擔(別的欄位改了,它也要跟著改)
  • 容易造成資料不一致(忘記改的話,資料就錯了)

所以,與其存一個會造成困擾的欄位,不如需要的時候再算就好。

刪除總價欄位

套用第三正規化形式,我們的訂房表單應該變成這樣:

會員 ID房型 ID入住天數
U001R0012
U001R0033
U002R0021
房型 IDR001
入住天數2
房型 IDR003
入住天數3
房型 IDR002
入住天數1

當你需要「總價」的時候,再去算:

  1. 從房型表單查到單價(例如 R001 的單價是 3000)
  2. 單價 × 入住天數 = 總價(3000 × 2 = 6000)

這樣就不會有資料不一致的問題了。

什麼時候可以保留可推算的欄位?

第三正規化形式並不是叫你「看到可推算的欄位就刪掉」。

有些情況下,你可能會刻意保留這些欄位。

計算邏輯不固定時可保留

如果「總價」不是單純的「單價 × 天數」,而是:

  • 可能有折扣
  • 可能有優惠活動
  • 可能有會員專屬價格
  • 可能有加購項目

這時候「總價」就不能單純從別的欄位算出來,因為計算邏輯會因人、因時而異。

在這種情況下,保留「總價」是合理的。

效能考量時可保留

如果計算總價需要查很多張表、做很複雜的運算,而且這個值經常被查詢,那你可能會選擇把它存起來,避免每次都重新計算。

但要記得:這是刻意違反正規化形式,你要能說出為什麼這樣做。

判斷標準

問自己一個問題:

這個欄位的值,是不是「永遠」可以從別的欄位算出來?

  • 如果是 → 可以刪掉
  • 如果不是(有例外情況)→ 保留

找出可推算欄位的練習

第三正規化形式其實就是在找「廢話」——那些你講了前兩句,就不用講第三句的情況。

來看幾個例子:

年齡可以從出生年算出來

「我出生於民國 100 年,今年是民國 124 年,我 24 歲。」

這三個資訊,你只需要其中兩個:

  • 知道「出生年」和「今年」→ 可以算出「年齡」
  • 知道「出生年」和「年齡」→ 可以算出「今年」

所以「年齡」是可推算的欄位,可以不用存。

是否破產可以從餘額算出來

「我的戶頭有 100 元,我要提領 120 元,我破產了。」

「是否破產」可以從「戶頭餘額」和「提領金額」推算出來:

  • 提領金額 > 戶頭餘額 → 破產

所以不需要另外存一個「是否破產」的欄位。

星期幾可以從日期算出來

「我抵達美國的時間是 2015 年 6 月 30 日,星期二。」

「星期幾」可以從「日期」算出來,所以不需要另外存。

第二正規化 vs 第三正規化

來比較一下這兩個正規化形式:

第二正規化形式第三正規化形式
問題同樣的資料重複出現很多次可以算出來的值另外存了一份
原因欄位之間有相依關係欄位的值可以計算出來
解法把相依的欄位拆到獨立的表單把可推算的欄位刪掉
例子會員 → 會員信箱單價 × 天數 → 總價
第二正規化形式同樣的資料重複出現很多次
第三正規化形式可以算出來的值另外存了一份
第二正規化形式欄位之間有相依關係
第三正規化形式欄位的值可以計算出來
第二正規化形式把相依的欄位拆到獨立的表單
第三正規化形式把可推算的欄位刪掉
第二正規化形式會員 → 會員信箱
第三正規化形式單價 × 天數 → 總價

兩個正規化形式的核心都是一樣的:避免重複資料。

只是第二正規化處理的是「直接的重複」,第三正規化處理的是「可推算的重複」。

第三正規化重點整理

這篇文章介紹了第三正規化形式的核心概念:

  1. 問題:有些欄位可以從別的欄位算出來,另外存一份就是重複
  2. 風險:別的欄位改了,這個欄位也要跟著改,容易造成資料不一致
  3. 第三正規化形式:如果欄位可以從別的欄位推算出來,就把它刪掉
  4. 例外情況:如果計算邏輯不固定,或有效能考量,可以保留
  5. 口訣:拆表單、刪欄位——第二正規化拆表單,第三正規化刪欄位

到目前為止,我們學的第二和第三正規化形式,都是從「欄位之間的相依關係」來決定要不要拆表單、刪欄位。

接下來的文章會介紹另一種角度:從「資料列」的層級來看,還有什麼需要調整的地方。

目前還沒有留言,成為第一個留言的人吧!

發表留言

留言將在審核後顯示。

資料庫

目錄

  • 第二正規化之後還有哪些多餘欄位
  • 總價是多餘的嗎?
  • 可推算欄位的問題
  • 房型漲價要改總價
  • 改天數也要改總價
  • 可推算的值也是一種重複
  • 第三正規化形式
  • 刪除總價欄位
  • 什麼時候可以保留可推算的欄位?
  • 計算邏輯不固定時可保留
  • 效能考量時可保留
  • 判斷標準
  • 找出可推算欄位的練習
  • 年齡可以從出生年算出來
  • 是否破產可以從餘額算出來
  • 星期幾可以從日期算出來
  • 第二正規化 vs 第三正規化
  • 第三正規化重點整理