前面的文章我們學會了:
- 如何找出個體和關係
- 為什麼要分成多張表
- 如何透過關聯避免重複資料
這篇文章會透過三個實際案例,從簡單到複雜,讓你練習如何把日常生活中的資訊轉換成資料表。你會發現,有些看似簡單的句子,背後其實藏著複雜的結構。
範例一:一句話拆成三張表
原始資訊
David 在 3 月 11 日去吃了「饗食天堂」這家吃到飽餐廳。
找出個體
這句話裡有哪些「東西」?
- David:一個人
- 饗食天堂:一家餐廳
人和餐廳是不同類型的東西,所以要分成兩張表。
找出關係
David 和餐廳之間有什麼關係?
- 用餐:David 去這家餐廳用餐
找出性質
- 餐廳有一個性質:餐廳類型(吃到飽)
- 用餐關係有一個性質:用餐日期(3 月 11 日)
設計資料表
設計資料表的順序是:先把表開好,再把性質加進去。
第一步:先開個體表
我們有兩種個體:人物和餐廳,所以先開兩張表。
人物表:
| 名稱 |
|---|
| David |
餐廳表:
| 名稱 |
|---|
| 饗食天堂 |
第二步:把個體的性質加進去
餐廳有一個性質「餐廳類型」,把它加到餐廳表:
餐廳表:
| 名稱 | 餐廳類型 |
|---|---|
| 饗食天堂 | 吃到飽 |
第三步:開關係表
David 和餐廳之間有「用餐」這個關係,所以開一張用餐表:
用餐表:
| 人物 | 餐廳 |
|---|---|
| David | 饗食天堂 |
第四步:把關係的性質加進去
用餐關係有一個性質「用餐日期」,把它加到用餐表:
用餐表:
| 人物 | 餐廳 | 用餐日期 |
|---|---|---|
| David | 饗食天堂 | 3/11 |
小結
短短一句話,就需要三張表來記錄:
- 人物表:記錄人物的基本資料
- 餐廳表:記錄餐廳的基本資料和類型
- 用餐表:記錄誰在什麼時候去哪家餐廳用餐
範例二:當關係指向另一個關係
原始資訊
胖虎欺負大雄這件事,引起了靜香的反感。
找出個體
- 胖虎、大雄、靜香:三個人
找出關係
這裡有兩個關係:
- 欺負:胖虎欺負大雄
- 反感:靜香對某件事反感
分析關係的結構
欺負關係:
「胖虎欺負大雄」可以抽象成「A 欺負 B」的結構。
這個關係是非對稱的:A 欺負 B 不等於 B 欺負 A。胖虎欺負大雄,不代表大雄欺負胖虎。
| 欺負的人 | 被欺負的人 |
|---|---|
| 胖虎 | 大雄 |
其他可能的情況:
| 欺負的人 | 被欺負的人 |
|---|---|
| 胖虎 | 大雄 |
| 小夫 | 大雄 |
| 大雄 | 哆啦A夢 |
反感關係:
靜香反感的是什麼?
- 不是反感胖虎這個人
- 不是反感大雄這個人
- 是反感「胖虎欺負大雄」這件事
也就是說,反感的對象是一個「關係」,不是一個「個體」。
還記得我們說過「個體和關係之間也可以有關係」嗎?這就是一個例子:
- 欺負關係:胖虎(個體)和大雄(個體)之間
- 反感關係:靜香(個體)和「欺負關係」(關係)之間
設計資料表
人物表:
| 名稱 |
|---|
| 胖虎 |
| 大雄 |
| 靜香 |
欺負表:
| 欺負的人 | 被欺負的人 |
|---|---|
| 胖虎 | 大雄 |
反感表:
| 反感的人 | 反感的對象 |
|---|---|
| 靜香 | 胖虎欺負大雄的關係 |
關聯的運用
注意:欺負表中的「胖虎」和「大雄」都會關聯回人物表。
如果胖虎有其他性質(例如:體重 80 公斤),需要記錄在欺負表嗎?
不需要。 回去查人物表就好。
這就是關聯的好處:每個人的資料只存在人物表一個地方,其他表只需要記錄「是誰」,詳細資料透過關聯查詢。
範例三:簡單的句子,複雜的結構
原始資訊
因為現在週年慶,全館商品免運。
這句話有多複雜?
表面上看起來很簡單,但仔細想想:
- 什麼是「商品」?
- 什麼是「運費」?
- 「免運」要怎麼記錄?
拆解「商品」的概念
「商品」這個詞其實包含了好幾個東西:
- 品項:球棒、球鞋…
- 消費者:誰要買這些東西
- 交易關係:消費者購買品項的這個動作
那「運費」呢?運費要放在哪裡?
仔細想想,運費其實也是一種「你要付錢的東西」,跟球棒、球鞋的性質很像。差別只在於:球棒是實體商品,運費是服務。
所以我們可以把運費也當成一種「品項」來處理:
- 品項:球棒、球鞋、運費…
- 消費者:誰要買這些東西
- 交易關係:消費者購買品項的這個動作
這樣一來,「免運」就變成很單純的事情:運費這個品項的金額設為 0 元。
第一版:單一交易表的設計
假設胖虎在 3 月 20 日買了一支球棒,我們可以這樣設計:
品項表:
| 品項名稱 | 價格 |
|---|---|
| 球棒 | 500 |
| 運費 | 60 |
交易表:
| 消費者 | 品項 | 下單日期 |
|---|---|---|
| 胖虎 | 球棒 | 3/20 |
| 胖虎 | 運費 | 3/20 |
問題:怎麼知道哪些品項是同一筆訂單?
胖虎買了球棒,也付了運費。但從上面的表格,我們怎麼知道這兩筆是「同一筆訂單」?
如果胖虎在 3 月 20 日買了兩次東西呢?
| 消費者 | 品項 | 下單日期 |
|---|---|---|
| 胖虎 | 球棒 | 3/20 |
| 胖虎 | 運費 | 3/20 |
| 胖虎 | 球鞋 | 3/20 |
| 胖虎 | 運費 | 3/20 |
哪個運費是球棒的?哪個運費是球鞋的?分不清楚了。
第二版:加入訂單編號識別同一筆交易
我們需要一個「訂單編號」來識別同一筆交易:
| 訂單編號 | 消費者 | 品項 | 下單日期 |
|---|---|---|---|
| 1 | 胖虎 | 球棒 | 3/20 |
| 1 | 胖虎 | 運費 | 3/20 |
| 2 | 胖虎 | 球鞋 | 3/20 |
| 2 | 胖虎 | 運費 | 3/20 |
現在可以分清楚了:訂單 1 包含球棒和運費,訂單 2 包含球鞋和運費。
問題:資料重複了
但是,你發現問題了嗎?
- 「消費者:胖虎」重複出現
- 「下單日期:3/20」重複出現
如果發現訂單 1 的消費者其實是靜香,不是胖虎,你要改幾個地方?兩個地方。
這就是我們之前說的「重複資料的問題」。
第三版:分表解決重複資料問題
解決方法:把「訂單資訊」和「訂單品項」分成兩張表。
訂單表:
| 訂單編號 | 消費者 | 下單日期 |
|---|---|---|
| 1 | 胖虎 | 3/20 |
| 2 | 胖虎 | 3/20 |
訂單品項表:
| 訂單編號 | 品項 |
|---|---|
| 1 | 球棒 |
| 1 | 運費 |
| 2 | 球鞋 |
| 2 | 運費 |
現在,如果要修改訂單 1 的消費者,只需要改訂單表的一個地方。
訂單品項表透過「訂單編號」關聯回訂單表,不需要重複儲存消費者和下單日期。
如何實現「免運」?
回到原本的問題:週年慶期間,全館商品免運。
現在我們有了完整的資料結構,實現免運就很簡單:
只要下單日期落在週年慶期間,該訂單的運費品項金額就設為 0 元。
或者,週年慶期間的訂單根本不需要新增運費這個品項。
小結
這篇文章透過三個範例,練習了資料結構化的過程:
範例一(一句話拆成三張表)學到:
- 先找出個體,再找出關係
- 先把表開好,再把性質加進去
範例二(當關係指向另一個關係)學到:
- 關係可以是非對稱的
- 反感的對象不一定是「人」,也可以是「事件」(例如:靜香反感的是「胖虎欺負大雄」這件事)
- 透過關聯查詢其他表的資料,不需要重複儲存
範例三(簡單的句子,複雜的結構)學到:
- 簡單的句子可能藏著複雜的結構
- 當資料重複時,就是需要分表的訊號
- 透過「編號」來關聯不同的表
設計資料表的基本流程:
- 找出個體 → 建立個體表
- 找出關係 → 建立關係表
- 找出性質 → 加到對應的表
- 發現重複 → 考慮分表
- 透過關聯 → 串接不同的表
把這個流程練熟,你就掌握了關聯式資料庫設計的基本功。