前置條件
- 兩表皆為 InnoDB 引擎(MyISAM 不支援外鍵)。
- 外鍵欄位**必須有索引**(5.6+ 建立外鍵自動創建,舊版需手動)。
- 關聯欄位**資料型別相容**(如
INT 對 INT、BIGINT 對 BIGINT,不可 INT 對 VARCHAR)。
建立外鍵語法
ALTER TABLE `child_table`
ADD CONSTRAINT `fk_name`
FOREIGN KEY (`child_col`)
REFERENCES `parent_table` (`parent_col`)
ON DELETE CASCADE
ON UPDATE CASCADE;
參照動作(ON DELETE / ON UPDATE)
| 動作 | 行為 | 適用場景 |
CASCADE | 父表刪除/更新 → 子表關聯行同步刪除/更新 | 強一致性,如 訂單-明細、文章-評論 |
SET NULL | 父表刪除/更新 → 子表外鍵欄位設 NULL(需欄位允許 NULL) | 保留子記錄但解除關聯,如 用戶-文章(用戶刪除文章保留) |
RESTRICT / NO ACTION | 拒絕父表刪除/更新(有子行存在時) | 保護主資料不被誤刪,預設行為 |
SET DEFAULT | InnoDB 不支援 | — |
實戰案例:文章-分類
建表
CREATE TABLE `category` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` CHAR(16) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `article` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`category_id` INT NOT NULL,
`name` CHAR(16) NOT NULL,
KEY `fk_cat` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
加外鍵(預設 RESTRICT)
ALTER TABLE `article`
ADD CONSTRAINT `fk_cat` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`);
測試預設行為
- 刪除分類
DELETE FROM category WHERE id=1 → 報錯 1451:Cannot delete or update a parent row。
- 新增不存在分類的文章
INSERT INTO article(category_id,name) VALUES(2,'x') → 報錯 1452:Cannot add or update a child row。
改為級聯刪除/更新
ALTER TABLE article DROP FOREIGN KEY fk_cat;
ALTER TABLE `article`
ADD CONSTRAINT `fk_cat` FOREIGN KEY (`category_id`)
REFERENCES `category` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE;
- 刪除分類 → 關聯文章同步刪除。
- 更新分類 ID → 文章外鍵同步更新。
常見錯誤代碼速查
- 1451:父表操作被子表記錄阻擋(RESTRICT/NO ACTION)。
- 1452:子表插入/更新的外鍵值在父表不存在。
- 1215:Cannot add foreign key constraint(型別不符、缺索引、引擎非 InnoDB、字元集/排序規則不一致)。
效能與架構建議
- 高並發寫入場景慎用外鍵(鎖競爭、死鎖風險),改由應用層維護一致性。
- 大表
ALTER TABLE ADD FOREIGN KEY 會鎖表,建議低峰期或用 pt-online-schema-change。
- 複寫環境下外鍵可能導致從庫 SQL 線程阻塞,評估風險後再啟用。