外键约束怎么加才生效
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。
