萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 外鍵約束完整指南:建立、級聯動作、刪除與 InnoDB 限制條件

前置條件(硬性要求)

  1. **引擎必須為 InnoDB**(MyISAM 不支援外鍵)。
  2. 父表與子表**對應列數據類型完全一致**(含長度、簽名、字符集、校對規則)。
  3. 父表被引用列**必須有索引**(主鍵、唯一索引、普通索引均可)。
  4. 子表外鍵列**必須有索引**(MySQL 會自動創建,若不存在)。
  5. 若顯式指定 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,行保留

效能與鎖影響

  • 外鍵檢查發生在**行鎖持有期間**,增加鎖持有時間。
  • CASCADE 級聯操作會**遞歸加鎖**子表,大量子行時易引發鎖升級、死鎖。
  • 外鍵列**必須建索引**,否則檢查父表引用時全表掃描,極慢且鎖全表。
  • 大批量導入/變更時,建議**暫時關閉外鍵檢查**:
    SET FOREIGN_KEY_CHECKS = 0;
    -- 大量 LOAD DATA / DELETE / UPDATE
    SET FOREIGN_KEY_CHECKS = 1;

常見錯誤代碼對照

錯誤碼 訊息 原因
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) 舊版本外鍵創建失敗通用碼,同上

最佳實踐建議

  1. **核心業務強一致性場景**使用外鍵(訂單-明細、帳戶-流水)。
  2. **高併發寫入、大批量導入、微服務跨庫**場景**不建議**使用外鍵,改由應用層/事件總線保證最終一致性。
  3. 必須使用時:顯式命名約束、顯式建立索引、優先 RESTRICT、謹慎 CASCADE、定期 CHECK TABLE 驗證完整性。
  4. 文檰化:在數據字典/ER 圖標註外鍵關係,避免隱性依賴。
copyright © 萬盛學電腦網 all rights reserved