~/VibeHandbook
免費 PDF

14 · 03

Schema 設計基礎

schema 是你資料的藍圖:存在哪些表、它們有哪些列,以及它們如何關聯。好的 schema 設計大多是常識加上幾個習慣。

  • 每一種"東西"對應一張表。 使用者、帖子、訂單——各自擁有自己的表。
  • 每一行都需要一個唯一的 ID(一個主鍵)。
  • 用外部索引鍵連結表,而不是到處複製資料。
  • 選擇誠實的型別。 金錢是 decimal,不是 float。日期是時間戳,不是文本。
  • 不要重複。 如果一個使用者的郵箱存在於五張表裡,你最終會有五個不同的郵箱。

下面是一個部落格的簡單 schema,正是 AI 會為你生成的那種:

CREATE TABLE users (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email       TEXT NOT NULL UNIQUE,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE posts (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    author_id   UUID NOT NULL REFERENCES users(id),
    title       TEXT NOT NULL,
    body        TEXT NOT NULL,
    published   BOOLEAN NOT NULL DEFAULT false,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX idx_posts_author ON posts(author_id);

author_id ... REFERENCES users(id) 這一行就是外部索引鍵。它把每篇帖子接回到恰好一個真實使用者,於是資料庫自身就會拒絕儲存一篇沒有有效作者的帖子:

   users                          posts
   ┌─────────────┐                ┌─────────────┐
   │ id  (PK) ◀──┼────────────────┤ author_id   │  (FK → users.id)
   │ email       │  一個使用者       │ id  (PK)    │
   │ created_at  │  擁有多篇       │ title       │
   └─────────────┘  帖子           │ body        │
                                   │ published   │
        1  ────────────────────▶  *└─────────────┘
       (一)                      (多)

注意 REFERENCES(一個把帖子繫結到真實使用者的外部索引鍵)以及 NOT NULLUNIQUE 約束。這些是資料庫為你強制執行的護欄,所以即使你的程式碼有 bug,壞資料也無法溜進來。

一個值得有意新增的約束,是當父記錄消失時會發生什麼。預設情況下,刪除一個仍被帖子引用的使用者會失敗——而這往往正是你想要的。但你可以顯式指定:REFERENCES users(id) ON DELETE CASCADE 會把帖子也一併刪除,而 ON DELETE RESTRICT 會阻止該刪除。有意去選,勝過在生產環境裡以慘痛方式發現預設行為。同樣的"讓資料庫保護你"的直覺也適用於 CHECK 約束(CHECK (price >= 0))——它在源頭拒絕荒謬的資料,而不是去信任每一條程式碼路徑都會做校驗。

想離線閱讀?

免費下載整本書的 PDF 或 EPUB。