建表報錯 ERROR 1005 (HY000): Can't create table ... (errno: 121/150) 是外鍵約束配置不當的典型表現。本文系統梳理錯誌代碼含義、根因及規範化建表姿勢。
| Errno | 常見提示 | 核心含義 |
|---|---|---|
| 121 | Duplicate key on write or update | 外鍵名稱在數據庫級別重複(外鍵名全局唯一) |
| 150 | Foreign key constraint is incorrectly formed | 外鍵定義不合法:類型不匹配、引用列無索引、字符集不一致等 |
錯誤場景: 兩張中間表 `role_privilege`、`user_role` 都引用 `role(id)`,且外鍵名均為 `role_id_FK`。
-- 錯誤
CREATE TABLE role_privilege (
role_id VARCHAR(40),
CONSTRAINT role_id_FK FOREIGN KEY (role_id) REFERENCES role(id)
);
CREATE TABLE user_role (
role_id VARCHAR(40),
CONSTRAINT role_id_FK FOREIGN KEY (role_id) REFERENCES role(id) -- 名稱衝突!
);
修正: 外鍵名需**全庫唯一**,建議採用 fk_表名_引用列_被引用表 命名規範:
CONSTRAINT fk_role_privilege_role_id_role FOREIGN KEY (role_id) REFERENCES role(id)
CONSTRAINT fk_user_role_role_id_role FOREIGN KEY (role_id) REFERENCES role(id)
INT,子表外鍵 CHAR/VARCHAR,或長度/符號位不一致(如 INT(11) vs BIGINT)。原則:精確一致,含 UNSIGNED。utf8mb4,子表 latin1,或校對規則不同(_general_ci vs _bin)。原則:庫、表、列三級字符集完全一致。-- 父表
CREATE TABLE `role` (
`id` VARCHAR(40) NOT NULL COMMENT 'UUID',
`name` VARCHAR(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 子表
CREATE TABLE `user_role` (
`user_id` VARCHAR(40) NOT NULL,
`role_id` VARCHAR(40) NOT NULL,
PRIMARY KEY (`user_id`, `role_id`),
-- 子表外鍵列自動創建索引,無需額外 INDEX
CONSTRAINT `fk_user_role_role_id_role` FOREIGN KEY (`role_id`)
REFERENCES `role` (`id`)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT `fk_user_role_user_id_user` FOREIGN KEY (`user_id`)
REFERENCES `user` (`id`)
ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
| 選項 | 父表 DELETE/UPDATE 時子表行為 |
|---|---|
| RESTRICT / NO ACTION | 阻止父表操作(默認),報錯 1451/1452 |
| CASCADE | 級聯刪除/更新子表匹配行 |
| SET NULL | 子表外鍵列置 NULL(需列允許 NULL) |
| SET DEFAULT | 置默認值(InnoDB 不支持,僅解析不生效) |
ON DELETE RESTRICT。理解 Errno 121/150 背後的約束規則,配合規範化命名與類型定義,能消除 90% 的建表報錯,為數據完整性打下堅實基礎。