约束
MySQL 约束:主键、唯一、非空、检查、默认值与外键;desc/show create 与插入报错实验。
本页目录
没有约束的表,可以插进 id=null、重复手机号、15 岁的「员工」……约束就是在数据库层给数据「上规矩」。
系列:MySQL 入门到查询 · 第 15 / 17 篇(第 15 天 · 5 月 5 日)
上一篇:多表关联查询
下一篇:备份与实用命令
总目录:系列索引
环境说明
- 库:
train - 本篇专用表在文中给出;请按小节 先删再建,避免旧结构干扰
- 练习数据以本篇 seed 为准
1. 什么是约束
约束(Constraint):对表中某一列(或多列)的数据增加的限制条件。
| 约束 | 关键字 | 一句话 |
|---|---|---|
| 主键 | primary key | 非空 + 唯一;一张表通常一个主键 |
| 唯一 | unique | 不能重复;多个 NULL 在 MySQL 的 UNIQUE 列里通常是允许的 |
| 非空 | not null | 不能为空 |
| 检查 | check(条件) | 值必须满足条件(如 age>=18;MySQL 需 8.0.16+ 才能真正强制) |
| 默认值 | default 值 | 不给该列值时用默认值 |
| 外键 | foreign key | 约定两张表的关联(强约束) |
2. 建一张「多约束」表
use train;
drop table if exists person;
create table person(
id int primary key,
name varchar(10) not null,
sex varchar(1) default '女',
age int check(age>=18),
phone varchar(11) unique
);
2.1 用 desc / show create 查看
desc person;
show create table person;
| 列 | 在 desc 里看什么 |
|---|---|
id | Null=NO,Key=PRI |
name | Null=NO |
sex | Default=女(或相近显示) |
phone | Key=UNI |
age 的 check | desc 可能看不到,看 show create table 里的 CONSTRAINT ... CHECK |
3. 逐个测约束(插入报错)
3.1 主键:非空且唯一
-- 失败:主键不能为 NULL
insert into person values(null,'张三','男',20,'13112345678');
ERROR 1048 (23000): Column 'id' cannot be null
insert into person values(1,'张三','男',20,'13112345678');
-- 失败:主键重复
insert into person values(1,'李四','女',19,'13212345678');
ERROR 1062 (23000): Duplicate entry '1' for key 'person.PRIMARY'
3.2 非空 not null
-- 失败:name 不能为 NULL
insert into person values(2,null,'女',19,'13212345678');
ERROR 1048 (23000): Column 'name' cannot be null
3.3 默认值 default(易错)
-- 全列插入时,显式写 null 往往就是 null,不一定触发 default
insert into person values(2,'李四',null,19,'13212345678');
select * from person;
-- 推荐:指定列插入,省略 sex,才会用默认值
insert into person(id,name,age,phone) values(3,'王五',21,'13312345678');
select * from person;
口诀:默认值在「没给这一列」时生效;用
insert into 表(列...) values(...)才能稳定吃到 default。
3.4 检查 check
-- 失败:age < 18
insert into person values(4,'赵六','男',15,'13412345678');
ERROR 3819 (HY000): Check constraint ... is violated.
insert into person values(4,'赵六','男',18,'13412345678');
3.5 唯一 unique
-- 失败:phone 与已有数据重复
insert into person values(5,'钱七','男',20,'13112345678');
ERROR 1062 (23000): Duplicate entry '13112345678' for key ...
insert into person values(5,'钱七','男',20,'13900000005');
select * from person;
4. 自动编号 auto_increment(配合主键)
主键常常由数据库自增,减少手写编号:
drop table if exists auto01;
create table auto01(
id int primary key auto_increment,
name varchar(10)
);
insert into auto01 values(null,'张三');
insert into auto01(name) values('李四');
insert into auto01 values(10,'王五'); -- 指定 id 也可以
insert into auto01(name) values('赵六');
select * from auto01;
desc auto01; -- Key=PRI,Extra 里可见 auto_increment
| 行为 | 说明 |
|---|---|
不给 id / 给 null | 用下一个自动编号 |
显式给较大 id | 后续自动编号一般从更大值继续 |
| 删除中间行 | 一般不会回填已删编号 |
规则:
auto_increment通常与 主键 一起用,不能单独当唯一业务号来「保证连续不跳号」。
5. 外键 foreign key
5.1 先分清主表 / 从表
| 角色 | 例子 | 说明 |
|---|---|---|
| 主表 | 学生表 s01 | 提供被引用的键;课堂常用主键(MySQL 也可引用 UNIQUE 列) |
| 从表 | 课程/成绩表 c01 | 用外键列指向主表 |
主表 s01(id 主键) ←── 从表 c01(sid → s01.id)
5.2 语法(写在从表)
constraint 外键名 foreign key(从表列) references 主表名(主表列)
建表顺序:先主后从
use train;
drop table if exists c01;
drop table if exists s01;
create table s01(
id int primary key,
name varchar(10)
);
create table c01(
cid int,
sid int,
cname varchar(10),
constraint s01_id_c01_sid foreign key(sid) references s01(id)
);
show create table c01;
desc c01 中 sid 的 Key 可能显示 MUL;具体外键关系看 show create table c01:
CONSTRAINT `s01_id_c01_sid` FOREIGN KEY (`sid`) REFERENCES `s01` (`id`)
5.3 使用规则(背下来)
| 操作 | 顺序 |
|---|---|
| 建表 | 先主表,后从表 |
| 插入 | 先主表数据,后从表数据 |
| 删除数据 | 先从表,后主表 |
| 删除表 | 先从表,后主表 |
| 修改 | 改完后仍要满足外键关系 |
| 查询 | 不受限制 |
5.4 实验:先插从表会失败
-- 错误:主表还没有 id=1
insert into c01 values(100,1,'语文');
ERROR 1452 ... Cannot add or update a child row: a foreign key constraint fails ...
insert into s01 values(1,'张三');
insert into c01 values(100,1,'语文'); -- cname 是课程名示例
select * from s01;
select * from c01;
5.5 实验:删/改主表被挡住
-- 失败:从表还有引用 id=1
update s01 set id=2 where id=1;
delete from s01 where id=1;
ERROR 1451 ... Cannot delete or update a parent row ...
正确顺序:
delete from c01 where sid=1;
delete from s01 where id=1;
5.6 实验:先 drop 主表会失败
drop table s01;
ERROR 3730 ... Cannot drop table 's01' referenced by a foreign key ...
drop table c01;
drop table s01;
了解:生产里有人选择「不用数据库外键、在应用层校验」以降低耦合;考试与课堂常仍要求会建、会按顺序增删。入门先把规则记牢。
6. 完整跟练脚本
use train;
drop table if exists person;
create table person(
id int primary key,
name varchar(10) not null,
sex varchar(1) default '女',
age int check(age>=18),
phone varchar(11) unique
);
desc person;
show create table person;
insert into person values(1,'张三','男',20,'13112345678');
insert into person(id,name,age,phone) values(2,'李四',19,'13212345678');
insert into person(id,name,age,phone) values(3,'王五',22,'13312345678');
select * from person;
-- 约束失败示例(注释可取消再执行观察报错)
-- insert into person values(1,'重复主键','男',20,'13911112222');
-- insert into person(id,name,age,phone) values(9,'未成年',17,'13922223333');
-- insert into person(id,name,age,phone) values(8,'李四',20,'13212345678');
drop table if exists auto01;
create table auto01(id int primary key auto_increment, name varchar(10));
insert into auto01(name) values('a'),('b');
select * from auto01;
drop table if exists c01;
drop table if exists s01;
create table s01(id int primary key, name varchar(10));
create table c01(
cid int,
sid int,
cname varchar(10),
constraint s01_id_c01_sid foreign key(sid) references s01(id)
);
insert into s01 values(1,'张三');
insert into c01 values(100,1,'语文');
select * from s01;
select * from c01;
-- delete from s01 where id=1; -- 会 1451
delete from c01 where sid=1;
delete from s01 where id=1;
drop table if exists c01;
drop table if exists s01;
7. 速查表
| 目标 | 写法 |
|---|---|
| 主键 | id int primary key |
| 非空 | name varchar(10) not null |
| 默认值 | sex varchar(1) default '女' |
| 检查 | age int check(age>=18) |
| 唯一 | phone varchar(11) unique |
| 自增 | id int primary key auto_increment |
| 看结构 | desc 表; / show create table 表; |
| 外键(从表) | constraint 名 foreign key(列) references 主表(主表列) |
| 建/插/删顺序 | 先主后从;删数据与删表先从后主 |
8. 今天的练习清单
- 创建
person,用desc/show create table找出各约束 - 主键:NULL、重复 id 各测一次
-
not null:name=null -
default:全列nullvs 指定列省略 sex -
check:age=15失败,age=18成功 -
unique:重复 phone -
auto_increment插入几行并观察 id - 外键:错误插入顺序 → 正确顺序 → 1451/3730 → 先从后主删除
常见错误
易错:以为
insert values(..., null, ...)会自动变成default——全列插入写 null 往往就是 null。
易错:主键与 unique 混淆:主键 = 非空 + 唯一;unique 主要强调唯一。
易错:外键列不在从表,或主表被引用列不是主键。
易错:先建从表、先删主表,导致报错。
易错:只desc找 check,看不到就以为没约束——用show create table。
易错:业务号依赖 auto_increment 保证「连续、可预测」。
小结
- 约束是数据库层的数据规矩:主键、唯一、非空、check、default、外键。
- 验证约束最快的方式:故意插错数据,读报错码。
- 默认值:指定列插入、不写该列,才最稳。
- 外键:先主后从建表与插入;删除则相反。
- 下一篇 备份与实用命令:mysqldump 等运维向内容。