前置條件(硬性要求)
- **引擎必須為 InnoDB**(MyISAM 不支援外鍵)。
- 父表與子表**對應列數據類型完全一致**(含長度、簽名、字符集、校對規則)。
- 父表被引用列**必須有索引**(主鍵、唯一索引、普通索引均可)。
- 子表外鍵列**必須有索引**(MySQL 會自動創建,若不存在)。
- 若顯式指定
CONSTRAINT symbol,**庫級唯一**。
語法標準形式
[CONSTRAINT symbol] FOREIGN KEY [index_name] (col1, col2, ...)
REFERENCES parent_tbl (col1, col2, ...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
Reference Options 對照表
| 選項 |
父表 DELETE/UPDATE 時子表行為 |
適用場景 |
RESTRICT (默認) |
拒絕父表操作,報錯 1217/1216 |
嚴格保護主數據,防誤刪 |
CASCADE |
自動級聯刪除/更新子表匹配行 |
明確從屬關係:訂單-明細、用戶-地址 |
SET NULL |
子表外鍵列置 NULL(列必須允許 NULL) |
弱關聯:文章-作者(可匿名)、任務-負責人 |
NO ACTION |
同 RESTRICT(標準 SQL 語義:延遲檢查) |
相容性 |
SET DEFAULT |
**InnoDB 不支援** |
-- |
建表時直接定義(推薦)
CREATE TABLE `parent` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE `child` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`parent_id` INT UNSIGNED NOT NULL,
`data` VARCHAR(100),
INDEX `idx_parent_id` (`parent_id`), -- 顯式建立,避免隱式創建命名混亂
CONSTRAINT `fk_child_parent` FOREIGN KEY (`parent_id`)
REFERENCES `parent` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE=InnoDB;
既有表添加外鍵(ALTER TABLE)
-- 1. 確保父表被引用列有索引(主鍵自帶)
-- 2. 確保子表外鍵列有索引(無則自動創建)
ALTER TABLE `child`
ADD CONSTRAINT `fk_child_parent`
FOREIGN KEY (`parent_id`)
REFERENCES `parent` (`id`)
ON DELETE RESTRICT
ON UPDATE CASCADE;
修改/刪除外鍵
-- 查看外鍵名
SHOW CREATE TABLE child;
-- 或
SELECT CONSTRAINT_NAME, TABLE_NAME, REFERENCED_TABLE_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'db' AND REFERENCED_TABLE_NAME IS NOT NULL;
-- 刪除(需知約束名)
ALTER TABLE `child` DROP FOREIGN KEY `fk_child_parent`;
-- 修改:必須先 DROP 再 ADD(無 ALTER FOREIGN KEY)
級聯操作實戰演示
-- 初始數據
INSERT INTO parent (name) VALUES ('A'), ('B');
INSERT INTO child (parent_id, data) VALUES (1, 'A-1'), (1, 'A-2'), (2, 'B-1');
-- CASE 1: ON UPDATE CASCADE
UPDATE parent SET id = 10 WHERE id = 1;
-- child.parent_id 自動變為 10
-- CASE 2: ON DELETE CASCADE
DELETE FROM parent WHERE id = 2;
-- child 中 parent_id=2 的行自動刪除
-- CASE 3: ON DELETE SET NULL (需子表列允許 NULL)
ALTER TABLE child MODIFY parent_id INT UNSIGNED NULL;
ALTER TABLE child DROP FOREIGN KEY fk_child_parent;
ALTER TABLE child ADD CONSTRAINT fk_child_parent FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET NULL;
DELETE FROM parent WHERE id = 10;
-- child.parent_id 變為 NULL,行保留
效能與鎖影響
常見錯誤代碼對照
| 錯誤碼 |
訊息 |
原因 |
| 1215 |
Cannot add foreign key constraint |
類型不匹配、父表無索引、引擎非 InnoDB、字符集校對不一致 |
| 1216 |
Cannot add or update a child row: a foreign key constraint fails |
插入/更新子表時父表無匹配值 |
| 1217 |
Cannot delete or update a parent row: a foreign key constraint fails |
刪除/更新父表時子表存在引用,且非 CASCADE/SET NULL |
| 1005 |
Can't create table (errno: 150) |
舊版本外鍵創建失敗通用碼,同上 |
最佳實踐建議
- **核心業務強一致性場景**使用外鍵(訂單-明細、帳戶-流水)。
- **高併發寫入、大批量導入、微服務跨庫**場景**不建議**使用外鍵,改由應用層/事件總線保證最終一致性。
- 必須使用時:顯式命名約束、顯式建立索引、優先
RESTRICT、謹慎 CASCADE、定期 CHECK TABLE 驗證完整性。
- 文檰化:在數據字典/ER 圖標註外鍵關係,避免隱性依賴。