參考完整性確保「子表外鍵值必須存在於父表主鍵中」,防止孤兒資料、維護業務邏輯一致性。MySQL 早期為效能捨棄外鍵,InnoDB 引擎(3.23.34+)引入完整支援,現為關係型資料庫標準配置。
-- 1. 父表:物種
CREATE TABLE species (
id TINYINT NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (id)
) ENGINE=INNODB;
INSERT INTO species VALUES
(1, 'orangutan'), (2, 'elephant'), (3, 'hippopotamus'), (4, 'yak');
-- 2. 子表:動物園動物
CREATE TABLE zoo (
id INT(4) NOT NULL,
name VARCHAR(50) NOT NULL,
fk_species TINYINT(4) NOT NULL,
INDEX (fk_species), -- 必須建立索引
FOREIGN KEY (fk_species) REFERENCES species (id),
PRIMARY KEY (id)
) ENGINE=INNODB;
-- 嘗試插入不存在的物種代碼 5
INSERT INTO zoo VALUES (1, 'Harry', 5);
-- ERROR 1216 (23000): Cannot add or update a child row: a foreign key constraint fails
-- 插入合法代碼 3 (hippopotamus)
INSERT INTO zoo VALUES (1, 'Harry', 3);
-- Query OK, 1 row affected
定義外鍵時可指定父表變更時的自動傳播行為:
FOREIGN KEY (fk_species) REFERENCES species (id)
ON DELETE RESTRICT -- 預設:父表有子記錄時禁止刪除
ON UPDATE CASCADE -- 父表 ID 變更時自動更新子表
-- 可選:CASCADE | SET NULL | NO ACTION | RESTRICT | SET DEFAULT
SHOW CREATE TABLE zoo\G
-- 輸出片段:
-- CONSTRAINT `zoo_ibfk_1` FOREIGN KEY (`fk_species`) REFERENCES `species` (`id`)
ALTER TABLE zoo DROP FOREIGN KEY zoo_ibfk_1;
ALTER TABLE zoo
ADD FOREIGN KEY (fk_species) REFERENCES species (id);
| 優勢 | 劣勢/成本 |
|---|---|
| 資料庫層強制保證完整性,應用層無需重複檢查 | 每次 INSERT/UPDATE/DELETE 額外索引查找與鎖開銷 |
| 級聯動作自動維護關聯,減少應用程式碼 | 大事務級聯可能導致長時間鎖爭用、死鎖風險 |
| Schema 自文檔化,ER 圖可直接反向生成 | 分庫分表、線上 DDL 變更極其複雜,常需先刪除外鍵 |
| 防止臭蟲資料入庫,降低資料清洗成本 | 大量批次匯入需暫時關閉 foreign_key_checks=0 |
ON DELETE CASCADE 易誤刪大量資料,建議預設 RESTRICT,刪除走應用層軟刪除/標記。pt-online-schema-change 免鎖變更。Innodb_row_lock_waits、Innodb_deadlocks、外鍵檢查耗時,納入儀表板。外鍵是資料庫層面的「契約鎖」,用微小的效能代價換取強有力的資料完整性保證。在 InnoDB 成熟的今天,核心業務表預設加外鍵應為團隊共識;唯有極致效能場景、經過壓測驗證,才考慮移至應用層。記住:資料不一致的修復成本,永遠遠超外鍵的維護成本。