核心概念

Entity(實體):業務領域裡有意義的「事物」,有自己的屬性。例:UserOrderProductInvoice

Attribute(屬性):描述實體的特性。Usernameemailcreated_at。分辨哪些屬性是識別符(Primary Key)是第一個設計決策。

Relationship(關係):實體之間的業務連結。User 下了 OrderOrder 包含 Product

Cardinality(基數):關係的「多少」——是一對一、一對多、還是多對多?


Cardinality 的四種情況

1:1(一對一):一個 User 有一個 Profile,一個 Profile 屬於一個 User
實作:通常合併成一張表,或把 FK 放在使用頻率較低的那邊。

1:N(一對多):一個 User 有多個 Order,每個 Order 只屬於一個 User
實作:FK 放在「多」的那邊(orders.user_id)。

M:N(多對多):一個 Order 包含多個 Product,一個 Product 可以出現在多個 Order
實作:需要中間表(order_items,帶 order_id + product_id)。中間表通常還有自己的屬性(數量、價格)。

1:0 or 1(可選的一對一)User 可能有 BillingAddress,也可能沒有。
實作:FK 允許 NULL,或拆成獨立表。


ER 圖的標記方式

常見的標記法有 Chen 符號、Crow’s Foot、UML Class Diagram。Crow’s Foot 最常見:

User ||--o{ Order : "places"
     └── 一個 User 必須有 0 或多個 Order

Order }o--|| Product : "contains"  
     └── Order 包含 1 個以上的 Product(透過中間表)

工具:dbdiagram.io(DBML 語法,export SQL)、draw.io(視覺化拖拉)、Mermaid ER diagram(在 Markdown 裡畫)。


常見的 ER Modeling 錯誤

把狀態放在實體名稱裡:設計了 ActiveUserInactiveUser——這是兩個 entity 還是一個 entity 的狀態?應該是 User + status 欄位。

把所有東西塞進一個大表orders 表裡放了 user 資訊、billing address、shipping address、每個商品的資訊——這個表沒有辦法做任何有效的關聯查詢,而且大量重複資料。

忽略關係本身的屬性User 加入了 Team——這個關係發生的時間、角色(admin / member)呢?這些屬性屬於 relationship,不屬於任何一個 entity,需要存在中間表 team_memberships 裡。

混淆事務和快照Order 裡要不要存 product_nameproduct_price?原則是:訂單是時間點的快照,商品名稱和價格在下單後可能改變,所以訂單裡要存當時的名稱和價格,不是只存 FK 然後每次 JOIN。


ER 圖到 SQL 的翻譯

-- User 1:N Order
CREATE TABLE users (
  id BIGINT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(255) UNIQUE NOT NULL
);
 
CREATE TABLE orders (
  id BIGINT PRIMARY KEY,
  user_id BIGINT NOT NULL REFERENCES users(id),
  created_at TIMESTAMPTZ DEFAULT NOW()
);
 
-- Order M:N Product(透過中間表)
CREATE TABLE products (
  id BIGINT PRIMARY KEY,
  name VARCHAR(200) NOT NULL,
  base_price DECIMAL(10, 2) NOT NULL
);
 
CREATE TABLE order_items (
  order_id BIGINT REFERENCES orders(id),
  product_id BIGINT REFERENCES products(id),
  quantity INT NOT NULL,
  unit_price DECIMAL(10, 2) NOT NULL,  -- 快照!記錄下單時的價格
  PRIMARY KEY (order_id, product_id)
);

ER Modeling 是設計的語言,SQL 是實作的語言。把這兩個步驟分開,設計討論在 ER 層進行,才不會一開始就陷入 SQL 的細節而忽略業務邏輯。