Logo

新人日誌

首頁關於我部落格

新人日誌

Logo

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

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

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

資料庫架構設計入門:用第一正規化處理一對多關係

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

前面的文章介紹了第二正規化和第三正規化,都是從「欄位之間的相依關係」來決定要不要拆表單、刪欄位。

這篇文章要介紹的第一正規化形式,角度不太一樣——我們要從「資料列」的層級來看,有沒有需要調整的地方。

先來看一個實際的例子。

一個會員有多支電話怎麼辦?

假設我們有一張會員表單:

會員會員信箱會員電話
Daviddavid@mail.com0912-345-678
Emilyemily@mail.com0923-456-789
會員信箱david@mail.com
會員電話0912-345-678
會員信箱emily@mail.com
會員電話0923-456-789

看起來很合理對吧?會員、會員信箱、會員電話,很正常。

但這張表單有一個隱藏的問題:如果會員有多支電話怎麼辦?

表單裡只有一個「會員電話」欄位,如果 David 有兩支、三支電話,要怎麼處理?

不要用多個欄位存多支電話

你可能會想:那我就多開幾個欄位啊!

會員會員信箱會員電話一會員電話二會員電話三會員電話四
Daviddavid@mail.com0912-345-6780922-111-222
Emilyemily@mail.com0923-456-789
會員信箱david@mail.com
會員電話一0912-345-678
會員電話二0922-111-222
會員電話三
會員電話四
會員信箱emily@mail.com
會員電話一0923-456-789
會員電話二
會員電話三
會員電話四

這樣就可以記錄四支電話了!

拜託不要這樣做。

增加欄位的問題

問題一:欄位數量永遠不夠

如果會員有五支電話怎麼辦?十支呢?你要一直開新欄位嗎?

還記得我們說過,關聯式資料庫很少允許「一直往右增加欄位」這件事。欄位的數量應該在設計時就固定下來,不能因為某個會員的電話變多了,就臨時加欄位。

問題二:查詢變得很複雜

如果你想知道「這個會員有幾支電話」,你要怎麼做?

你必須把這十個欄位全部讀出來,然後一個一個檢查哪些有值、哪些是空的。

如果你想從一支電話找到它屬於哪個會員呢?

你要把所有會員的十個電話欄位全部讀出來,一個一個比對,才能找到是哪一筆資料。

這樣的查詢非常沒有效率,而且很容易出錯。

不要把多支電話塞在同一格

你可能又會想:那我不要增加欄位,我把所有電話都塞在同一格裡面!

會員會員信箱會員電話們
Daviddavid@mail.com0912-345-678, 0922-111-222
Emilyemily@mail.com0923-456-789
會員信箱david@mail.com
會員電話們0912-345-678, 0922-111-222
會員信箱emily@mail.com
會員電話們0923-456-789

這樣就不用一直開新欄位了!

這樣也不行。

塞在同一格的問題

問題一:很難確保資料格式一致

這一格裡面可能有人用逗號分隔,有人用空格分隔,有人用換行分隔。你很難確保每一筆資料的格式都一樣。

問題二:處理資料很麻煩

如果你想新增一支電話,你要:

  1. 把整格資料讀出來
  2. 解析裡面的內容
  3. 加上新的電話
  4. 重新組合成字串
  5. 整筆塞回去

如果你想知道這個會員有幾支電話,你也要把整格讀出來、解析、計算。

相比之下,如果每支電話是獨立的一筆資料,新增和查詢都會簡單很多。

用拆表單解決一對多關係

第一正規化形式告訴我們:遇到一對多的關係,就拆表單。

什麼是「一對多」?就是「一個會員可以有多支電話」這種情況。

我們把電話拆成獨立的表單:

會員表單:

會員 ID會員名稱會員信箱
U001Daviddavid@mail.com
U002Emilyemily@mail.com
會員名稱David
會員信箱david@mail.com
會員名稱Emily
會員信箱emily@mail.com

會員電話表單:

電話會員 ID
0912-345-678U001
0922-111-222U001
0933-333-444U001
0923-456-789U002
會員 IDU001
會員 IDU001
會員 IDU001
會員 IDU002

現在 David 有三支電話,我們只要在「會員電話表單」裡新增三筆資料就好。

如果他之後又新增了一百支電話,我們就再新增一百筆資料。完全不需要動到欄位的設計。

拆表單的好處

新增電話很簡單

要新增電話?就在「會員電話表單」新增一筆資料,填上電話和會員 ID 就好。

查詢電話數量很簡單

要知道 David 有幾支電話?從「會員電話表單」篩選出「會員 ID = U001」的資料,數一數有幾筆就好。

從電話找會員很簡單

要從一支電話找到會員?從「會員電話表單」找到那支電話,就能看到它的「會員 ID」是什麼。

欄位往右長 vs 資料往下長

這裡有一個很重要的觀念:

在關聯式資料庫裡,資料列往下增加是正常的,但欄位往右增加要非常謹慎。

錯誤的做法(往右長):

會員電話一電話二電話三電話四…

正確的做法(往下長):

電話會員 ID
0912-345-678U001
0922-111-222U001
0933-333-444U001
……
會員 IDU001
會員 IDU001
會員 IDU001
會員 ID…

拆表單之後,原本想要「往右長」的欄位,變成了「往下長」的資料列。這才是關聯式資料庫正確的設計方式。

第一正規化形式

第一正規化形式(First Normal Form,簡稱 1NF)的核心概念是:

每個欄位只存一個值,遇到一對多的關係就拆表單。

具體來說:

  1. 不要為了「同性質的多筆資料」開很多欄位(例如電話一、電話二、電話三)
  2. 不要在同一格裡塞很多筆資料(例如用逗號分隔多支電話)
  3. 遇到一對多關係,就拆成獨立的表單

什麼時候可以違反第一正規化?

當然,有時候你可能會刻意違反這個原則。

用 JSON 格式存多筆資料

現在很多資料庫已經支援 JSON 格式。

什麼是 JSON?簡單來說,它是一種資料格式,可以在一個欄位裡存放「結構化的資料」,包括清單。

舉個例子,你的會員表單可以這樣設計:

會員 ID會員名稱會員電話
U001David[“0912-345-678”, “0922-111-222”, “0933-333-444”]
U002Emily[“0923-456-789”]
會員名稱David
會員電話[“0912-345-678”, “0922-111-222”, “0933-333-444”]
會員名稱Emily
會員電話[“0923-456-789”]

「會員電話」這個欄位存的是一個 JSON 陣列,裡面可以放很多支電話。

這樣做的好處是:不用另外開一張「會員電話表單」,設計上比較簡單。

JSON 和逗號分隔的字串有什麼差異?

你可能會問:這跟前面說的「把多支電話塞在同一格」有什麼不一樣?

前面的做法是用逗號分隔的字串:

0912-345-678, 0922-111-222, 0933-333-444

這只是一串文字,資料庫不知道裡面有幾筆資料,也不知道怎麼拆開它。你要自己寫程式去解析。

JSON 格式則是:

["0912-345-678", "0922-111-222", "0933-333-444"]

這是一個「結構化的清單」,資料庫知道裡面有三筆資料。支援 JSON 的資料庫可以直接:

  • 查詢「這個清單裡有沒有某支電話」
  • 取得「這個清單裡的第一筆資料」
  • 計算「這個清單裡有幾筆資料」

簡單來說,逗號分隔的字串只是文字,JSON 則是資料庫看得懂的結構。

但即使是 JSON,它的查詢效率還是比不上拆表單。所以 JSON 適合「不常查詢」的附屬資料,如果需要頻繁查詢,還是拆表單比較好。

什麼情況適合用 JSON?

如果你的情境符合以下條件,可以考慮用 JSON:

  • 電話只是附屬資訊:你不需要經常單獨查詢電話
  • 不需要反向查詢:你不需要「從電話找到會員」這種操作
  • 資料量不大:每個會員的電話數量有限,不會有幾百支
  • 你確定這樣可以簡化設計:拆表單反而讓系統變得太複雜

什麼情況不適合用 JSON?

如果你需要:

  • 經常查詢「某支電話屬於哪個會員」
  • 統計「所有會員總共有幾支電話」
  • 對電話做排序、篩選、分組等操作

那就不適合用 JSON,還是乖乖拆表單比較好。

重點:知道規則,才能打破規則

用 JSON 存多筆資料,是刻意違反第一正規化形式。

這沒有不對,但你要知道自己在做什麼。你知道這個原則,評估過利弊之後,選擇不遵守。

這跟「不知道有這個原則,隨便亂設計」是完全不一樣的。

第一正規化重點整理

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

  1. 問題:一個會員有多支電話,要怎麼存?
  2. 錯誤做法一:開很多欄位(電話一、電話二…)→ 欄位數量永遠不夠,查詢很複雜
  3. 錯誤做法二:把多筆資料塞在同一格 → 格式難統一,處理很麻煩
  4. 正確做法:拆表單,讓資料往下長,而不是欄位往右長
  5. 第一正規化形式:每個欄位只存一個值,遇到一對多關係就拆表單

到目前為止,我們已經學了三個正規化形式:

  • 第一正規化:一對多關係要拆表單
  • 第二正規化:相依的欄位放同一張表單
  • 第三正規化:可以算出來的欄位要刪掉

這三個原則的核心都是一樣的:避免重複資料,保持資料一致。

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

發表留言

留言將在審核後顯示。

資料庫

目錄

  • 一個會員有多支電話怎麼辦?
  • 不要用多個欄位存多支電話
  • 增加欄位的問題
  • 不要把多支電話塞在同一格
  • 塞在同一格的問題
  • 用拆表單解決一對多關係
  • 拆表單的好處
  • 新增電話很簡單
  • 查詢電話數量很簡單
  • 從電話找會員很簡單
  • 欄位往右長 vs 資料往下長
  • 第一正規化形式
  • 什麼時候可以違反第一正規化?
  • 用 JSON 格式存多筆資料
  • JSON 和逗號分隔的字串有什麼差異?
  • 什麼情況適合用 JSON?
  • 什麼情況不適合用 JSON?
  • 重點:知道規則,才能打破規則
  • 第一正規化重點整理