MySQL 建立外鍵時報 ERROR 150 (HY000): Can't create table ... (errno: 150) 是經典難題,錯誤訊息不提示具體原因。本文整理**九大常見原因**、排查順序、驗證 SQL 與解決方案,助你快速定位修復。
| # | 原因 | 排查/驗證 SQL | 解決方案 |
|---|---|---|---|
| 1 | 欄位類型/長度/符號不匹配 如 INT(10) vs INT(11)、INT vs BIGINT、SIGNED vs UNSIGNED | SHOW CREATE TABLE parent_table;SHOW CREATE TABLE child_table;對比欄位定義**完全一致** | 修正子表欄位定義與父表主鍵**完全一致** (類型、長度、UNSIGNED、CHARSET、COLLATION) |
| 2 | 父表欄位無索引/非主鍵 被引用欄位必須是 PRIMARY KEY 或有 INDEX | SHOW INDEX FROM parent_table WHERE Column_name = 'ref_col'; | 父表欄位加 PRIMARY KEY 或 INDEX idx_ref_col (ref_col) |
| 3 | 外鍵名稱衝突 資料庫級唯一,同名外鍵已存在 | SELECT CONSTRAINT_NAME FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_TYPE='FOREIGN KEY'; | 指定唯一名稱:CONSTRAINT fk_child_parent_unique FOREIGN KEY ... |
| 4 | 表引擎不支援外鍵 MyISAM 不支援,必須 InnoDB | SHOW TABLE STATUS LIKE 'table_name'; → Engine 欄位 | 轉換引擎:ALTER TABLE t ENGINE=InnoDB; |
| 5 | ON DELETE SET NULL 但欄位 NOT NULL | SHOW COLUMNS FROM child_table LIKE 'fk_col'; → Null: No | 修改欄位 ALTER TABLE child MODIFY fk_col INT NULL; 或改 ON DELETE CASCADE/RESTRICT |
| 6 | 字元集/排序規則不一致 父子表/欄位 CHARSET/COLLATE 不同 | SHOW CREATE TABLE parent_table;SHOW CREATE TABLE child_table; | 統一表/欄位 CHARSET (建議 utf8mb4) 與 COLLATE (utf8mb4_unicode_ci) |
| 7 | 外鍵欄位有 DEFAULT 值 如 DEFAULT 0 干擾外鍵語義 | SHOW COLUMNS FROM child_table LIKE 'fk_col'; → Default: 0 | 移除 DEFAULT:ALTER TABLE child ALTER COLUMN fk_col DROP DEFAULT; |
| 8 | 引用複合索引非最左列 父表複合索引 (a,b),子表引用 b | SHOW INDEX FROM parent_table; 檢查 Seq_in_index | 父表為被引用欄位建單獨索引,或調整複合索引順序 |
| 9 | ALTER TABLE 語法錯誤 語法拼寫、關鍵字順序 | 對照官方文檔核對 | 使用標準語法:ALTER TABLE child ADD CONSTRAINT fk_name FOREIGN KEY (col) REFERENCES parent(col) ON DELETE CASCADE; |
ALTER TABLE `child_table`
ADD CONSTRAINT `fk_child_parent_unique_name`
FOREIGN KEY (`parent_id`)
REFERENCES `parent_table` (`id`)
ON DELETE RESTRICT -- 或 CASCADE / SET NULL / NO ACTION
ON UPDATE CASCADE; -- 建議 CASCADE,主鍵變更自動同步
1. 檢查兩表引擎 → 必須都是 InnoDB
2. 檢查父表被引用欄位 → 必須有索引 (PK/UNIQUE/INDEX)
3. 對比父子欄位定義 → 類型、長度、UNSIGNED、CHARSET、COLLATE 完全一致
4. 檢查外鍵名稱 → 資料庫級唯一
5. 檢查 ON DELETE/UPDATE 子句與欄位 NOT NULL 衝突
6. 檢查是否引用複合索引非最左列
7. 使用標準 ALTER TABLE 語法
8. 仍失敗 → 查看錯誤日誌: SHOW ENGINE INNODB STATUS\G (LATEST FOREIGN KEY ERROR 區塊)
SHOW ENGINE INNODB STATUS\G
-- 關鍵區塊:
------------------------
LATEST FOREIGN KEY ERROR
------------------------
2024-01-15 10:30:00 0x... Transaction:
TRANSACTION 12345, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
FOREIGN KEY ERROR: ... (具體原因文字描述)
此輸出會直接提示具體原因(如「Data type mismatch」、「Parent table has no index」等)。
fk_子表_父表_欄位,全庫唯一。ERROR 150 九成以上源於 **欄位定義不一致**、**父表無索引**、**引擎非 InnoDB**、**字元集不匹配**。按本文九大原因順序排查,配合 SHOW ENGINE INNODB STATUS 看內部錯誤詳情,可在分鐘級定位解決。**規範先行、工具生成、謹慎使用外鍵**,是長久之計。