MySQL 報錯 ERROR 1005 (HY000): Can't create table ... (errno: 150) 是外鍵創建失敗的通用提示,實際原因隱藏在細節中。本文彙總 9 大高發原因,按概率排序,附帶排查 SQL 與修復方案。
現象:父表 INT(10),子表 INT(11);或一個 SIGNED 一個 UNSIGNED。
排查:
SHOW CREATE TABLE parent_table;
SHOW CREATE TABLE child_table;
-- 重點對比:類型、長度、UNSIGNED/SIGNED
修復:ALTER TABLE child_table MODIFY col INT(10) UNSIGNED; 保持完全一致。
規則:被引用列必須是主鍵,或有唯一索引/普通索引(InnoDB 要求)。
修復:
ALTER TABLE parent_table ADD INDEX idx_fk_col (fk_col);
-- 或
ALTER TABLE parent_table ADD PRIMARY KEY (fk_col);
現象:同一數據庫下外鍵名稱重複(即使跨表)。
排查:
SELECT CONSTRAINT_NAME FROM information_schema.TABLE_CONSTRAINTS
WHERE CONSTRAINT_TYPE = 'FOREIGN KEY' AND TABLE_SCHEMA = 'your_db';
修復:顯式指定唯一名稱 CONSTRAINT fk_unique_name FOREIGN KEY ...
現象:任一表為 MyISAM(不支持外鍵約束)。
排查:
SHOW TABLE STATUS WHERE Name IN ('parent', 'child');
-- 檢查 Engine 列
修復:
ALTER TABLE parent_table ENGINE=InnoDB;
ALTER TABLE child_table ENGINE=InnoDB;
現象:外鍵定義 ON DELETE SET NULL,但子表關聯列 NOT NULL。
修復:
-- 方案一:允許 NULL
ALTER TABLE child_table MODIFY fk_col INT NULL;
-- 方案二:改為 CASCADE/RESTRICT
ALTER TABLE child_table DROP FOREIGN KEY fk_name;
ALTER TABLE child_table ADD CONSTRAINT fk_name FOREIGN KEY ... ON DELETE CASCADE;
現象:表級或列級 CHARSET/COLLATE 不同(如 utf8 vs utf8mb4,或 _general_ci vs _unicode_ci)。
排查:
SHOW CREATE TABLE parent_table;
SHOW CREATE TABLE child_table;
-- 對比 DEFAULT CHARSET、列級 CHARSET
修復:統一為 utf8mb4 + utf8mb4_unicode_ci。
現象:如 fk_col INT DEFAULT 0,MySQL 某版本禁止外鍵列有默認值。
修復:ALTER TABLE child_table ALTER fk_col DROP DEFAULT;
現象:父表 INDEX idx_a_b (a, b),外鍵引用 b 列。
規則:被引用列必須是索引的最左前綴。
修復:ALTER TABLE parent_table ADD INDEX idx_b (b);
現象:引號、逗號、關鍵字拼寫錯誤。
修復:參考官方文檔標準語法:
ALTER TABLE child_table
ADD CONSTRAINT fk_name
FOREIGN KEY (fk_col) REFERENCES parent_table (pk_col)
ON DELETE CASCADE ON UPDATE CASCADE;
SELECT
tc.CONSTRAINT_NAME,
tc.TABLE_NAME AS child_table,
kcu.COLUMN_NAME AS child_column,
tc.REFERENCED_TABLE_NAME AS parent_table,
kcu.REFERENCED_COLUMN_NAME AS parent_column,
rc.UPDATE_RULE,
rc.DELETE_RULE
FROM information_schema.TABLE_CONSTRAINTS tc
JOIN information_schema.KEY_COLUMN_USAGE kcu
ON tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME
JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
ON tc.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
WHERE tc.CONSTRAINT_TYPE = 'FOREIGN KEY'
AND tc.TABLE_SCHEMA = 'your_db';
複雜 ER 關係建議用建模工具(MySQL Workbench、PowerDesigner、MyEclipse DDL 生成)正向設計,避免手寫 DDL 易漏細節。
核心口訣:類型長度符號一致、父列必有索引、引擎必須 InnoDB、字符集統一、名稱全局唯一。逐項核對 99% 可解。