本文 AI 產出,尚未審核
MySQL
單元:外鍵與約束 Constraint
主題:UNIQUE 與 DEFAULT
簡介
在關聯式資料庫中,資料完整性是最基礎也最重要的概念之一。若資料表允許任意、重複或不合規的值寫入,後續的查詢、統計甚至業務邏輯都可能出現錯誤。MySQL 提供了多種約束(Constraint)來協助開發者在資料寫入階段就自動驗證,UNIQUE 與 DEFAULT 就是其中最常用且最實用的兩種。
- UNIQUE 用來保證欄位(或欄位組合)的值在整個表格中不會重複,常見於身份識別碼、電子郵件等必須唯一的欄位。
- DEFAULT 則在插入資料時,若未提供該欄位的值,會自動填入事先定義好的預設值,減少程式端的判斷與錯誤。
掌握這兩個約束的使用方式,能讓你的資料庫設計更安全、程式碼更簡潔,也能提升系統的可維護性。本篇將從概念說明、實作範例、常見陷阱到最佳實踐,逐步帶你建立正確的 UNIQUE 與 DEFAULT 用法。
核心概念
1. UNIQUE 約束
| 特性 | 說明 |
|---|---|
| 唯一性 | 欄位值在整張表中只能出現一次,除 NULL 之外的值皆受限。 |
| 多欄位唯一鍵 | 可同時在多個欄位上建立唯一鍵,組合值必須唯一。 |
| 自動建立索引 | MySQL 會為 UNIQUE 鍵自動建立唯一索引,提升查詢效能。 |
1.1 單欄位 UNIQUE
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
username VARCHAR(50) NOT NULL,
-- 為 email 加上唯一約束,確保每個使用者只能有一個 email
UNIQUE KEY uq_email (email)
);
UNIQUE KEY uq_email (email):在email欄位上建立唯一索引,若嘗試插入已存在的 email,會拋出Duplicate entry錯誤。
1.2 多欄位組合唯一鍵
CREATE TABLE product_stock (
product_id INT NOT NULL,
store_id INT NOT NULL,
qty INT DEFAULT 0,
-- 同一商品在同一家門市的記錄只能出現一次
UNIQUE KEY uq_product_store (product_id, store_id)
);
- 這裡
product_id+store_id的組合必須唯一,避免同一商品在同一家門市出現兩筆庫存記錄。
1.3 使用 ALTER TABLE 加上 UNIQUE
-- 已有的表格,事後新增唯一約束
ALTER TABLE users
ADD CONSTRAINT uq_username UNIQUE (username);
- 若已有資料,MySQL 會先檢查所有現有資料是否違反唯一性,若有衝突則會失敗。
2. DEFAULT 約束
| 特性 | 說明 |
|---|---|
| 預設值 | 在 INSERT 時未提供欄位值時,自動使用預設值。 |
| 支援函式 | 可以使用 CURRENT_TIMESTAMP、NOW() 等時間函式作為預設值。 |
| 與 NOT NULL 結合 | 常與 NOT NULL 同時使用,確保欄位永遠有值。 |
2.1 基本 DEFAULT
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
status若在 INSERT 時未指定,會自動填入'pending'。created_at使用CURRENT_TIMESTAMP,自動紀錄建立時間。
2.2 數值與布林型別的 DEFAULT
CREATE TABLE products (
product_id INT AUTO_INCREMENT PRIMARY KEY,
price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
is_active TINYINT(1) NOT NULL DEFAULT 1 -- 1 代表啟用
);
- 這裡
price預設為0.00,is_active預設為1(即啟用)。
2.3 使用函式作為預設值
CREATE TABLE sessions (
session_id CHAR(36) NOT NULL PRIMARY KEY,
user_id INT NOT NULL,
expires_at DATETIME NOT NULL DEFAULT (NOW() + INTERVAL 1 DAY)
);
expires_at會在插入時自動設定為「現在時間 + 1 天」,適合用於暫存或登入過期時間。
2.4 ALTER TABLE 增加/修改 DEFAULT
-- 為已存在的欄位加上預設值
ALTER TABLE products
MODIFY COLUMN is_active TINYINT(1) NOT NULL DEFAULT 0;
- 變更預設值時,已存在的資料不會自動更新,只有未來的 INSERT 會受到影響。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決或最佳做法 |
|---|---|---|
| UNIQUE 與 NULL | MySQL 允許多筆 NULL 通過唯一約束,因為 NULL 被視為「未知」而非相等。 |
若需要「只能有一筆」的 NULL,可改用 UNIQUE + NOT NULL 或使用觸發器自行檢查。 |
| ALTER 時的衝突 | 在已有資料的表格上加 UNIQUE,若有重複值會直接失敗。 | 先執行 SELECT 找出重複資料,清理或合併後再加約束。 |
| DEFAULT 與自動遞增 | DEFAULT 不能與 AUTO_INCREMENT 同時使用,因為遞增值會覆寫預設。 |
只在非遞增欄位使用 DEFAULT,或在需要預設值的欄位上使用觸發器。 |
| 時間函式的預設值 | 在 MySQL 5.6 以前,DEFAULT CURRENT_TIMESTAMP 只能用於 TIMESTAMP,而非 DATETIME。 |
使用 MySQL 5.6+ 或改用 TIMESTAMP;若必須是 DATETIME,可在 INSERT 時使用函式。 |
| 多欄位唯一鍵的欄位順序 | 組合唯一鍵的欄位順序會影響索引的使用情況。 | 把查詢最常使用的欄位放在前面,以提升查詢效率。 |
最佳實踐小結
- 先設計再實作:在建立表格前先思考哪些欄位需要唯一性、哪些欄位需要預設值。
- 使用有意義的索引名稱:例如
uq_email、uq_product_store,方便日後維護。 - 結合 NOT NULL:對於必須有值且有預設值的欄位,請同時加上
NOT NULL,避免意外寫入NULL。 - 定期檢查唯一鍵的衝突:使用
SELECT column, COUNT(*) FROM table GROUP BY column HAVING COUNT(*) > 1;監控資料品質。 - 在程式層面捕捉例外:INSERT/UPDATE 時捕獲
Duplicate entry或Data truncated等錯誤,提供友善訊息給使用者。
實際應用場景
| 場景 | 使用的約束 | 為什麼需要 |
|---|---|---|
| 使用者註冊 | UNIQUE(email), DEFAULT('active') |
確保每個電子郵件只能註冊一次,且新使用者預設為啟用狀態。 |
| 訂單系統 | DEFAULT(CURRENT_TIMESTAMP) 在 order_date |
自動紀錄訂單產生時間,免除程式自行傳入時間的錯誤。 |
| 庫存管理 | 多欄位 UNIQUE(product_id, warehouse_id) |
防止同一商品在同一倉庫出現多筆庫存紀錄,保持資料一致性。 |
| 會話/Token | DEFAULT(NOW() + INTERVAL 30 MINUTE) 在 expire_at |
設定會話自動過期時間,提升安全性。 |
| 產品上架 | DEFAULT(0.00) 在 price、DEFAULT(1) 在 is_visible |
若未填寫價格或上架狀態,系統自動給予合理預設,避免 NULL 造成的計算錯誤。 |
總結
- UNIQUE 為資料表提供「唯一性」保證,適用於身份識別、鍵值或任何不容重複的欄位。
- DEFAULT 則在資料寫入時自動補齊缺失的值,減少程式端的判斷與錯誤。
- 正確使用這兩個約束可以讓資料庫在寫入階段即完成第一層驗證,提升系統可靠度與維護效率。
- 在實務開發中,務必先規劃好唯一鍵與預設值的需求,搭配
NOT NULL、適當的索引名稱與例外處理,才能發揮 MySQL 約束機制的最大效益。
掌握了 UNIQUE 與 DEFAULT 的概念與實作,你就能在 MySQL 中打造更安全、可靠且易於維護的資料模型。祝你在資料庫設計之路上越走越順!