外键约束仅InnoDB生效,MyISAM不校验;建表需父表被引用列有索引、列类型严格一致、外键名唯一;一对多在“多”方加外键并建索引;多对多必须用带双外键的中间表;LEFT JOIN出NULL常因孤儿记录。

外键约束怎么加才生效
MySQL 中表关联的核心是外键(FOREIGN KEY),但不是所有存储引擎都支持。只有 InnoDB 支持完整外键约束,MyISAM 虽能解析 FOREIGN KEY 语法,但完全不校验、不生效。
建表时加外键必须满足几个硬性条件:
-
父表的被引用列(通常是主键或唯一索引列)必须有明确的索引,否则报错ERROR 1005 (HY000): Can't create table ... errno: 150 - 子表和父表的对应列类型要严格一致:整型宽度、是否
UNSIGNED、字符集和排序规则(COLLATE)都不能差一点 - 外键名在同一个数据库内不能重复;若用
ALTER TABLE添加,需先确保子表数据全部符合父表现有值(否则会因“孤儿记录”失败)
一对多关系怎么写最稳妥
这是最常见也最容易出错的场景。比如 orders 表和 users 表,一个用户可下多个订单,但一个订单只属于一个用户。
关键设计点:
- 在「多」的一方(
orders)加外键字段,例如user_id INT NOT NULL,再用FOREIGN KEY (user_id) REFERENCES users(id) - 务必给
user_id加普通索引(INDEX),否则JOIN或DELETE CASCADE时性能极差 - 如果业务允许用户注销后订单仍保留,外键可设为
ON DELETE SET NULL;但此时user_id字段必须允许NULL,且注意后续WHERE user_id = ?查询无法走索引(NULL值不参与 B+ 树索引)
多对多关系为什么必须用中间表
比如 students 和 courses,一个学生选多门课,一门课被多个学生选。直接在任一表加字段会爆炸式冗余——你没法在一个字段里存多个 ID,也不该用逗号分隔字符串(违反第一范式,无法索引、无法约束、无法原子更新)。
正确做法是建第三张表(如 student_courses):
- 它只有两个字段:
student_id和course_id,联合设为主键(PRIMARY KEY (student_id, course_id)) - 两边都加外键:
FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE,同理处理course_id - 如果需要记录选课时间等额外信息,就把这些字段加到中间表里,而不是硬塞进任一主表
LEFT JOIN 时 NULL 值从哪来
很多人执行 SELECT * FROM orders LEFT JOIN users ON orders.user_id = users.id 后发现 users 字段全是 NULL,第一反应是“关联没写对”,其实更可能是外键字段里存了父表不存在的值(比如 user_id = 999,但 users 表最大 id 是 100)。
这种数据叫“孤儿记录”,根源在于:
- 建表时没加外键约束(或用了
MyISAM) - 应用层绕过数据库逻辑,直接
INSERT了非法user_id - 手动删了
users记录,但没配ON DELETE CASCADE,也没在代码里同步清理orders
查孤儿记录可以用:SELECT * FROM orders WHERE user_id NOT IN (SELECT id FROM users WHERE id IS NOT NULL),但注意 NOT IN 遇到 NULL 会整个失效,生产环境建议改用 NOT EXISTS 或 LEFT JOIN ... WHERE users.id IS NULL。










