当前位置: 代码网 > it编程>数据库>Mysql > 从 NULL 到外键:MySQL 表约束体系全解析

从 NULL 到外键:MySQL 表约束体系全解析

2026年10月09日 • Mysql •我要评论
mysql的表约束,就是在建表时给字段加上的规则,用来保证数据的正确性、完整性和唯一性,不满足规则的数据,数据库会直接拒绝写入。1. 引言在 mysql 中,真正约束字段的是数据类型,但数据类型约束很

mysql的表约束,就是在建表时给字段加上的规则,用来保证数据的正确性、完整性和唯一性,不满足规则的数据,数据库会直接拒绝写入。

1. 引言

在 mysql 中,真正约束字段的是数据类型,但数据类型约束很单一,无法满足复杂的业务需求。比如一个 email 字段要求唯一,或者一个班级名字段不允许为空,这些都需要额外的约束来保证数据的合法性,从业务逻辑角度保证数据的正确性。

表的约束有很多,本文主要介绍以下几个:

  • null / not null(空属性)
  • default(默认值)
  • comment(列描述)
  • zerofill(零填充)
  • primary key(主键)
  • auto_increment(自增长)
  • unique key(唯一键)
  • foreign key(外键)

2. 空属性(null / not null)

空属性有两个值:null(默认的)和 not null(不为空)。

数据库默认字段基本都是可以为空的,但是实际开发时,应尽可能保证字段不为空,因为数据为空没办法参与运算。

2.1 案例:创建班级表

创建一个班级表,包含班级名和班级所在的教室。站在正常的业务逻辑中:

  • 如果班级没有名字,你不知道你在哪个班级;
  • 如果教室名字可以为空,就不知道在哪上课。

所以我们在设计数据库表的时候,一定要在表中进行限制,满足上面条件的数据就不能插入到表中,这就是"约束"。

mysql> select null;
+------+
| null |
+------+
| null |
+------+
1 row in set (0.00 sec)

mysql> select 1+null;
+--------+
| 1+null |
+--------+
|   null |
+--------+
1 row in set (0.00 sec)

创建班级表,两个字段都设置为 not null:

mysql> create table myclass(
    -> class_name varchar(20) not null,
    -> class_room varchar(10) not null);
query ok, 0 rows affected (0.02 sec)

mysql> desc myclass;
+------------+-------------+------+-----+---------+-------+
| field      | type        | null | key | default | extra |
+------------+-------------+------+-----+---------+-------+
| class_name | varchar(20) | no   |     | null    |       |
| class_room | varchar(10) | no   |     | null    |       |
+------------+-------------+------+-----+---------+-------+

可以看到 null 列显示为 no,表示这两个字段不允许为空。此时如果插入数据时没有给教室字段赋值,就会插入失败:

mysql> insert into myclass(class_name) values('class1');
error 1364 (hy000): field 'class_room' doesn't have a default value

3. 默认值(default)

默认值:某一种数据会经常性地出现某个具体的值,可以在一开始就指定好,在需要真实数据的时候,用户可以选择性地使用默认值。

默认值的生效:数据在插入的时候不给该字段赋值,就使用默认值。

mysql> create table tt10 (
    -> name varchar(20) not null,
    -> age tinyint unsigned default 0,
    -> sex char(2) default '男'
    -> );
query ok, 0 rows affected (0.00 sec)

mysql> desc tt10;
+-------+---------------------+------+-----+---------+-------+
| field | type                | null | key | default | extra |
+-------+---------------------+------+-----+---------+-------+
| name  | varchar(20)         | no   |     | null    |       |
| age   | tinyint(3) unsigned | yes  |     | 0       |       |
| sex   | char(2)             | yes  |     | 男      |       |
+-------+---------------------+------+-----+---------+-------+

插入数据时只给 name 赋值,age 和 sex 会自动使用默认值:

mysql> insert into tt10(name) values('zhangsan');
query ok, 1 row affected (0.00 sec)

mysql> select * from tt10;
+----------+------+------+
| name     | age  | sex  |
+----------+------+------+
| zhangsan |    0 | 男   |
+----------+------+------+

注意:只有设置了 default 的列,才可以在插入值的时候对列进行省略。另外,not null 和 default 一般不需要同时出现,因为 default 本身有默认值,不会为空。

4. 列描述(comment)

列描述:comment,没有实际含义,专门用来描述字段,会根据表创建语句保存,用来给程序员或 dba 进行了解。

mysql> create table tt12 (
    -> name varchar(20) not null comment '姓名',
    -> age tinyint unsigned default 0 comment '年龄',
    -> sex char(2) default '男' comment '性别'
    -> );

通过 desc 查看不到注释信息,需要通过 show create table 查看:

mysql> show create table tt12\g
*************************** 1. row ***************************
       table: tt12
create table: create table `tt12` (
  `name` varchar(20) not null comment '姓名',
  `age` tinyint(3) unsigned default '0' comment '年龄',
  `sex` char(2) default '男' comment '性别'
) engine=myisam default charset=gbk
1 row in set (0.00 sec)

5. zerofill(零填充)

刚开始学习数据库时,很多人对数字类型后面的长度很迷茫。通过 show 看看 tt3 表的建表语句:

mysql> show create table tt3\g
***************** 1. row *****************
       table: tt3
create table: create table `tt3` (
  `a` int(10) unsigned default null,
  `b` int(10) unsigned default null
) engine=myisam default charset=gbk
1 row in set (0.00 sec)

可以看到 int(10),这个代表什么意思呢?整型不是 4 字节吗?这个 10 又代表什么呢?其实没有 zerofill 这个属性,括号内的数字是毫无意义的。

插入数据并查询:

mysql> insert into tt3 values(1,2);
query ok, 1 row affected (0.00 sec)

mysql> select * from tt3;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
+------+------+

对 a 列添加 zerofill 属性后,显示的结果就有所不同了:

mysql> alter table tt3 change a a int(5) unsigned zerofill;
query ok, 0 rows affected (0.00 sec)

mysql> show create table tt3\g
*************************** 1. row ***************************
       table: tt3
create table: create table `tt3` (
  `a` int(5) unsigned zerofill default null,  --具有了zerofill
  `b` int(10) unsigned default null
) engine=myisam default charset=gbk
1 row in set (0.00 sec)

mysql> select * from tt3;
+-------+------+
| a     | b    |
+-------+------+
| 00001 |    2 |
+-------+------+

这次可以看到 a 的值由原来的 1 变成 00001,这就是 zerofill 属性的作用:如果宽度小于设定的宽度(这里设置的是 5),自动填充 0。

注意:这只是最后显示的结果,在 mysql 中实际存储的还是 1。为什么是这样呢?我们可以用 hex 函数来证明:

mysql> select a, hex(a) from tt3;
+-------+--------+
| a     | hex(a) |
+-------+--------+
| 00001 | 1      |
+-------+--------+

可以看出数据库内部存储的还是 1,00001 只是设置了 zerofill 属性后的一种格式化输出而已。

6. 主键(primary key)

主键:primary key 用来唯一地约束该字段里面的数据,不能重复,不能为空,一张表中最多只能有一个主键;主键所在的列通常是整数类型。

6.1 创建表时指定主键

mysql> create table tt13 (
->  id int unsigned primary key comment '学号不能为空',
->  name varchar(20) not null);
query ok, 0 rows affected (0.00 sec)

mysql> desc tt13;
+-------+------------------+------+-----+---------+-------+
| field | type             | null | key | default | extra |
+-------+------------------+------+-----+---------+-------+
| id    | int(10) unsigned | no   | pri | null    |       | <= key 中 pri表示该字段是主键
| name  | varchar(20)      | no   |     | null    |       |
+-------+------------------+------+-----+---------+-------+

主键约束:主键对应的字段中不能重复,一旦重复,操作失败:

mysql> insert into tt13 values(1, 'aaa');
query ok, 1 row affected (0.00 sec)

mysql> insert into tt13 values(1, 'aaa');
error 1062 (23000): duplicate entry '1' for key 'primary'

6.2 追加与删除主键

当表创建好以后但是没有主键的时候,可以再次追加主键:

alter table 表名 add primary key(字段列表)

删除主键:

alter table 表名 drop primary key;

mysql> alter table tt13 drop primary key;
query ok, 0 rows affected (0.02 sec)

mysql> desc tt13;
+-------+------------------+------+-----+---------+-------+
| field | type             | null | key | default | extra |
+-------+------------------+------+-----+---------+-------+
| id    | int(10) unsigned | no   |     | null    |       |
| name  | varchar(20)      | no   |     | null    |       |
+-------+------------------+------+-----+---------+-------+

6.3 复合主键

在创建表的时候,在所有字段之后,使用 primary key(主键字段列表) 来创建主键,如果有多个字段作为主键,可以使用复合主键:

mysql> create table tt14(
-> id int unsigned,
-> course char(10) comment '课程代码',
-> score tinyint unsigned default 60 comment '成绩',
-> primary key(id, course) -- id和course为复合主键
-> );
query ok, 0 rows affected (0.01 sec)

mysql> desc tt14;
+--------+---------------------+------+-----+---------+-------+
| field  | type                | null | key | default | extra |
+--------+---------------------+------+-----+---------+-------+
| id     | int(10) unsigned    | no   | pri | 0       |       | <= 这两列合成主键
| course | char(10)            | no   | pri |         |       |
| score  | tinyint(3) unsigned | yes  |     | 60      |       |
+--------+---------------------+------+-----+---------+-------+

复合主键要求组合值不能重复:

mysql> insert into tt14 (id,course)values(1, '123');
query ok, 1 row affected (0.02 sec)

mysql> insert into tt14 (id,course)values(1, '123');
error 1062 (23000): duplicate entry '1-123' for key 'primary' -- 主键冲突

7. 自增长(auto_increment)

auto_increment:当对应的字段不给值时,系统会自动触发,从当前字段中已经有的最大值 +1 操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。

7.1 自增长的特点

  • 任何一个字段要做自增长,前提是本身是一个索引(key 一栏有值);
  • 自增长字段必须是整数;
  • 一张表最多只能有一个自增长。

7.2 案例

mysql> create table tt21(
    -> id int unsigned primary key auto_increment,
    -> name varchar(10) not null default ''
    -> );

mysql> insert into tt21(name) values('a');
mysql> insert into tt21(name) values('b');

mysql> select * from tt21;
+----+------+
| id | name |
+----+------+
|  1 | a    |
|  2 | b    |
+----+------+

在插入后获取上次插入的 auto_increment 的值(批量插入获取的是第一个值):

mysql> select last_insert_id();
+------------------+
| last_insert_id() |
+------------------+
|                1 |
+------------------+

7.3 关于索引

在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。

索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的 sql 语句执行得更快,可快速访问数据库表中的特定信息。

8. 唯一键(unique key)

一张表中有往往有很多字段需要唯一性,数据不能重复,但是一张表中只能有一个主键:唯一键就可以解决表中有多个字段需要唯一性约束的问题。

唯一键的本质和主键差不多,唯一键允许为空,而且可以多个为空,空字段不做唯一性比较。

8.1 唯一键与主键的区别

我们可以简单理解成:主键更多的是标识唯一性的,而唯一键更多的是保证在业务上不要和别的信息出现重复。

8.2 案例

比如在公司,我们需要一个员工管理系统,系统中有一个员工表,员工表中有两列信息:一个身份证号码,一个是员工工号。我们可以选择身份证号码作为主键。而我们设计员工工号的时候,需要一种约束:所有的员工工号都不能重复,具体指的是在公司的业务上不能重复。我们设计表的时候需要这个约束,那么就可以将员工工号设计成为唯一键。

一般而言,我们建议将主键设计成为和当前业务无关的字段,这样当业务调整的时候,我们可以尽量不会对主键做过大的调整。

mysql> create table student (
    -> id char(10) unique comment '学号,不能重复,但可以为空',
    -> name varchar(10)
    -> );
query ok, 0 rows affected (0.01 sec)

mysql> insert into student(id, name) values('01', 'aaa');
query ok, 1 row affected (0.00 sec)

mysql> insert into student(id, name) values('01', 'bbb'); --唯一约束不能重复
error 1062 (23000): duplicate entry '01' for key 'id'

mysql> insert into student(id, name) values(null, 'bbb'); -- 但可以为空
query ok, 1 row affected (0.00 sec)

mysql> select * from student;
+------+------+
| id   | name |
+------+------+
| 01   | aaa  |
| null | bbb  |
+------+------+

9. 外键(foreign key)

外键用于定义主表和从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或 unique 约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为 null。

9.1 语法

foreign key (字段名) references 主表(列)

9.2 案例

先创建主键表(班级表):

create table myclass (
    id int primary key,
    name varchar(30) not null comment '班级名'
);

再创建从表(学生表),通过外键关联班级表:

create table stu (
    id int primary key,
    name varchar(30) not null comment '学生名',
    class_id int,
    foreign key (class_id) references myclass(id)
);

正常插入数据:

mysql> insert into myclass values(10, 'c++大牛班'),(20, 'java大神班');
query ok, 2 rows affected (0.03 sec)
records: 2  duplicates: 0  warnings: 0

mysql> insert into stu values(100, '张三', 10),(101, '李四',20);
query ok, 2 rows affected (0.01 sec)
records: 2  duplicates: 0  warnings: 0

插入一个班级号为 30 的学生,因为没有这个班级,所以插入不成功:

mysql> insert into stu values(102, 'wangwu',30);
error 1452 (23000): cannot add or update a child row:
a foreign key constraint fails (mytest.stu, constraint stu_ibfk_1
foreign key (class_id) references myclass (id))

插入班级 id 为 null,比如来了一个学生,目前还没有分配班级:

mysql> insert into stu values(102, 'wangwu', null);
query ok, 1 row affected (0.01 sec)

9.3 如何理解外键约束

首先我们承认,这个世界上的数据很多都是相关性的。理论上,上面的例子我们不创建外键约束,就正常建立学生表以及班级表,该有的字段我们都有。此时在实际使用的时候,可能会出现什么问题?

有没有可能插入的学生信息中有具体的班级,但是该班级却没有在班级表中?比如只开了 100 班、101 班,但是在上课的学生里面竟然有 102 班的学生(这个班目前并不存在),这很明显是有问题的。

因为此时两张表在业务上是有相关性的,但是在业务上没有建立约束关系,那么就可能出现问题。解决方案就是通过外键完成的。建立外键的本质其实就是把相关性交给 mysql 去审核了,提前告诉 mysql 表之间的约束关系,那么当用户插入不符合业务逻辑的数据的时候,mysql 不允许你插入。

10. 综合案例:商城购物系统

下面通过一个完整的商城购物系统,把前面学到的所有约束综合运用起来。

有一个商店的数据,记录客户及购物情况,由以下三个表组成:

  • 商品 goods(商品编号 goods_id,商品名 goods_name,单价 unitprice,商品类别 category,供应商 provider)
  • 客户 customer(客户号 customer_id,姓名 name,住址 address,邮箱 email,性别 sex,身份证 card_id)
  • 购买 purchase(购买订单号 order_id,客户号 customer_id,商品号 goods_id,购买数量 nums)

要求:

  • 每个表的主外键;
  • 客户的姓名不能为空值;
  • 邮箱不能重复;
  • 客户的性别(男,女)。
-- 创建数据库
create database if not exists bit32mall
default character set utf8 ;

-- 选择数据库
use bit32mall;

-- 创建数据库表
-- 商品
create table if not exists goods
(
    goods_id  int primary key auto_increment comment '商品编号',
    goods_name varchar(32) not null comment '商品名称',
    unitprice  int  not null default 0  comment '单价,单位分',
    category  varchar(12) comment '商品分类',
    provider  varchar(64) not null comment '供应商名称'
);

-- 客户
create table if not exists customer
(
    customer_id  int primary key auto_increment comment '客户编号',
    name varchar(32) not null comment '客户姓名',
    address  varchar(256)  comment '客户地址',
    email  varchar(64)  unique key comment '电子邮箱',
    sex  enum('男','女') not null comment '性别',
    card_id char(18) unique key comment '身份证'
);

-- 购买
create table if not exists purchase
(
    order_id  int primary key auto_increment comment '订单号',
    customer_id int comment '客户编号',
    goods_id  int  comment '商品编号',
    nums  int  default 0 comment '购买数量',
    foreign key (customer_id) references customer(customer_id),
    foreign key (goods_id) references goods(goods_id)
);

在这个综合案例中,我们综合运用了本文介绍的所有约束:

  • primary key:三张表都以自增长的整数作为主键;
  • auto_increment:主键字段自动增长,无需手动赋值;
  • not null:商品名、供应商、客户姓名、性别等业务关键字段不允许为空;
  • default:单价、购买数量等字段设置了默认值;
  • unique key:客户邮箱、身份证号在业务上不能重复;
  • foreign key:购买表通过外键关联客户表和商品表,保证订单引用的客户和商品真实存在。

11. 总结

本文详细介绍了 mysql 中常用的表约束:

  • 空属性(null / not null):控制字段是否允许为空,实际开发中应尽量保证字段不为空;
  • 默认值(default):为经常出现的值预先设定,插入时可省略该字段;
  • 列描述(comment):为字段添加说明,方便程序员和 dba 理解;
  • 零填充(zerofill):当数值宽度不足时自动补 0,仅影响显示不影响存储;
  • 主键(primary key):唯一标识一行数据,不能重复、不能为空,一张表最多一个;
  • 自增长(auto_increment):自动生成递增的整数,通常与主键搭配;
  • 唯一键(unique key):保证业务字段不重复,但允许为空;
  • 外键(foreign key):建立表与表之间的关联,保证数据的完整性。

约束的本质,是把业务逻辑的合法性交给数据库去审核,从而在源头保证数据的正确性。合理运用这些约束,是设计高质量数据库表的基础。

到此这篇关于从 null 到外键:mysql 表约束体系全解析的文章就介绍到这了,更多相关mysql表的约束内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

赞 (0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com