iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
JavaScript

不要再說你不會後端!30 天 Node.js 降維打擊指南 系列 第 18 篇

#Day 18:打通任督二脈 — SQL 關聯設計 (一對多與多對多)

  • 分享至 

  • xImage
  •  

一口氣推進到了 Day 18!在經過前幾天 SQL 基礎與 ORM 的洗禮後,今天我們要來處理關聯式資料庫最核心、也最精華的篇章。

如果資料庫只有單一張表格,那就只是個普通的 Excel 檔;真正讓 SQL 強大到能支撐起全球金融與電商系統的,正是它處理「關聯 (Relationships)」的能力。今天我們要來搞懂後端面試必考的兩種核心設計。

1. 什麼是外鍵 (Foreign Key)?

在講關聯之前,必須先認識「外鍵」。
假設我們有 Users (使用者) 和 Orders (訂單) 兩張表。為了知道哪筆訂單是誰買的,我們會在 Orders 表裡面加上一個 user_id 欄位。這個指向另一張表主鍵 (Primary Key) 的欄位,就稱為外鍵。它像是資料庫裡的「超連結」,把兩張表死死地綁在一起,並確保資料的一致性(你不能新增一筆屬於不存在使用者的幽靈訂單)。


2. 一對多 (One-to-Many, 1:N)

這是實務上最常見的關聯。

  • 場景:一個「使用者」可以擁有多筆「訂單」,但一筆「訂單」只能屬於一個「使用者」。
  • 設計法:在「多」的那一方(Orders)建立外鍵,指向「一」的那一方(Users)。

Prisma ORM 實作範例:

model User {
  id      Int      @id @default(autoincrement())
  name    String
  // 一個 User 可以有多筆 Order (陣列)
  orders  Order[]  
}

model Order {
  id      Int      @id @default(autoincrement())
  total   Int
  // 每一筆 Order 都有一個擁有者 (單一 User)
  userId  Int
  user    User     @relation(fields: [userId], references: [id])
}

當你要查詢使用者的所有訂單時,只要設定 include: { orders: true },ORM 就會自動幫你用底層的 JOIN 語法把兩張表的資料完美合併並回傳。


3. 多對多 (Many-to-Many, N:M)

這是初學者最容易卡關的地方。

  • 場景:一個「學生」可以選修多門「課程」,而一門「課程」也會有多個「學生」來上。
  • 致命錯誤:你不能在學生表存一堆課程 ID,也不能在課程表存一堆學生 ID,SQL 不允許在單一欄位塞入無限陣列(違反了第一正規化)。
  • 解法:中介表 (Junction Table)。我們必須無中生有第三張表,例如叫做 StudentCourse (選課紀錄表),裡面只存 student_id 和 course_id。

Prisma ORM 實作範例:
現代 ORM 最棒的地方在於,你甚至不需要自己手動去寫那張煩人的中介表!

model Student {
  id      Int      @id @default(autoincrement())
  name    String
  // 隱式多對多:直接互相指定陣列
  courses Course[] 
}

model Course {
  id      Int      @id @default(autoincrement())
  title   String
  // 隱式多對多:直接互相指定陣列
  students Student[]
}

當你要寫入「學生 A 選了 Node.js 課程」時,Prisma 會自動在底層幫你把關聯 ID 寫進隱藏的中介表裡,完全封裝了複雜的底層邏輯。


4. 為什麼這很重要?

當你掌握了 1:N 和 N:M,你就能設計出幾乎 90% 的現代應用程式資料庫架構:

  • 購物車系統:使用者 1:N 訂單,訂單 N:M 商品。
  • 部落格系統:作者 1:N 文章,文章 N:M 標籤。

今天我們徹底征服了 SQL 的關聯設計。明天 (Day 19),我們將轉換口味,進入 NoSQL 的世界,看看 MongoDB 是如何用完全不同的「降維打擊」思維,來處理這些複雜關聯的!


上一篇
Day17
系列文
不要再說你不會後端!30 天 Node.js 降維打擊指南 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言