MySQL 表的约束

1.1 概念

1.1.1为什么需要约束

数据类型本身也是一种约束,但是数据类型的限制比较单一,我们还需要额外的约束规则,从业务逻辑层面保障数据合法、正确。 比如身份证字段,除了字符类型限制,还要求整条记录里面身份证不能重复,单纯依靠字段类型无法实现这类需求。

字段类型无法覆盖的需求:

  • 字段是否允许为 NULL
  • 字段是否可以设置默认值
  • 给字段增加说明注释
  • 数字字段的业务范围限制
  • 设置字段作为行的唯一标识

MySQL 是数据存储方案,除了存放数据,还要尽可能保障数据安全,降低人为误操作带来的脏数据。

补充:约束不只是数据库层面可以实现,后端业务代码里面同样可以增加判断校验。数据库约束是底层兜底方案。

1.1.2约束是什么

约束是在数据表上强制执行的数据校验规则。本质是 MySQL 通过限制用户的增删改操作,维护数据安全与数据完整性。

数据完整性分为四类:

  1. 实体完整性:同一张表中,不能存在两条完全一样、无法区分的记录。
  2. 域完整性:限定字段取值范围,例如年龄 0~120,性别只能是男 / 女。
  3. 引用完整性:关联表之间的数据合法性,例如员工的部门编号,在部门表中必须存在。
  4. 用户自定义完整性:根据业务自定义规则,如用户名唯一、密码非空;部门经理薪资不能高于本部门普通员工平均工资的 5 倍。

约束属于数据库对象,保存在系统表中,拥有独立的约束名称。

MySQL 常用约束类型:

  • 非空约束 NOT NULL
  • 默认值约束 DEFAULT
  • 主键约束 PRIMARY KEY
  • 外键约束 FOREIGN KEY
  • 唯一约束 UNIQUE
  • 检查约束 CHECK

约束创建时机:

  1. 创建表的时候同步定义约束
  2. 表创建完成之后,修改表结构追加约束

约束分为列级约束、表级约束:

  • 列级约束:写在字段定义的后面,只对当前这一列生效
  • 表级约束:所有字段定义完成之后再声明,可以支持多字段联合约束

1.2 语法

列级约束语法:在定义列的同时定义约束

复制代码
create table 表名(列名 类型 约束);

表级约束语法:所有列定义完毕后定义约束

复制代码
create table 表名 (
    列名1 类型,
    列名2 类型,
    constraint 约束名 约束类型(列信息)
);

建表之后新增约束通用语法:

复制代码
alter table 表名 add constraint 约束名 约束类型(要约束的列名);

约束命名推荐规范:表名_列名_约束类型简写,方便后期维护、查找、删除约束。

1.3 非空约束 NOT NULL

作用:限定某个字段的值不允许为空 。 字段有两种状态:NULL(默认,允许为空)、NOT NULL(不允许为空)。

默认情况下字段允许为空。开发中核心业务字段尽量设置非空,因为 NULL 值无法正常参与运算。

重点区分:所有数据类型都可以存 NULL;空字符串 ''、数字 0 都不等于 NULL。

业务场景举例:班级表包含班级名称、教室名称。班级名称必须填写,没有班级名这条记录没有意义;教室名称可以暂时留空,表示教室还未分配。这类需求就需要非空约束实现。

建表示例:

复制代码
create table class (
    id int,
    class_name varchar(30) not null,
    room varchar(30)
);

建表之后修改非空约束

复制代码
-- change方式,字段名需要写两次
alter table student1 change column id id int not null;

-- modify方式(更推荐)
alter table student1 modify name varchar(30) not null;

说明:modify 语句中如果省略 not null,代表该字段恢复为允许为空。

⚠️ 注意:修改约束前要检查表内已有数据,如果该字段已经存在 NULL,直接设置 NOT NULL 会报错,修改失败。

删除非空约束(改为允许为空)

复制代码
alter table student1 modify id int null;
-- 简写写法
alter table student1 modify name varchar(30);

1.4 默认值约束 DEFAULT

作用:给字段预先设置默认值。插入数据时,如果不给这个字段显式赋值,自动填充默认值。 生效条件:插入语句没有给该字段赋值。

注意:同一个字段同时设置 not null + default,not null 不会拦截插入;不赋值自动使用默认值,此时 not null 约束意义不大。

示例:

复制代码
create table test2(
    id int not null,
    name varchar(30) not null,
    sex char(2) default '女' not null
);

插入数据时不指定 sex 字段,sex 会自动填充默认值 女。

建表后追加默认值 场景:建表时 age 没有设置默认值,业务中新录入学员大部分年龄都是 18 岁,后期追加默认值

复制代码
alter table test3 modify age int unsigned default 18;

1.5 列描述 ------ COMMENT

comment 是字段注释,没有校验约束功能,只用来描述字段含义,相当于 C/C++ 里面的注释。 注释信息会保存在建表语句中,但是 desc 表名 无法查看注释,必须使用 show create table 表名 查看完整建表语句。

示例:

复制代码
create table user_info (
    id int primary key comment '用户编号',
    id_card varchar(18) comment '身份证号码'
);

1.6 主键约束 PRIMARY KEY

数据库除了存储数据,还需要快速检索数据。主键就是一条记录的唯一标识,类似学号、编号,可以通过主键精准定位一条记录。

主键特点:

  1. 主键字段不能为空、不能重复
  2. 一张表最多只能有一个主键
  3. 主键字段通常选用整型
  4. 主键是项目开发中使用最频繁的约束

补充:创建主键的时候,MySQL 会自动为主键建立索引,基于主键查询速度更快;删除主键约束,对应的主键索引也会同步删除。业务上尽量不要修改主键的值,主键是行的唯一标识,修改主键容易破坏数据完整性。

建表时添加主键(列级)

复制代码
create table test5(id int primary key, name varchar(30) not null);

主键不能重复、不能为空,违反规则插入直接报错。

建表之后追加主键

复制代码
alter table test2 add primary key(id);

⚠️ 注意:如果目标字段里面已经存在重复数据,无法添加主键,需要先清理重复数据。

删除主键

复制代码
alter table test2 drop primary key;

复合主键(联合主键,表级约束) 多个字段组合在一起作为主键,组合整体不能重复,单独一列的值允许重复。适合 IP + 端口这类场景:

复制代码
create table test6(
    id int,
    hostname varchar(10),
    ip varchar(20),
    port int unsigned,
    primary key(ip,port)
);

1.7 主键自增长 AUTO_INCREMENT

作用:搭配主键使用,实现字段自动递增。不给该字段赋值时,系统读取当前字段最大值,+1 生成新值。

自增长约束硬性要求:

  1. 自增长字段必须是整数类型
  2. 字段必须是主键或者唯一键(字段具备唯一性,key 列有索引)
  3. 一张表里面最多只能有一个自增长字段
  4. 自增长字段隐含 NOT NULL
  5. 支持手动插入指定值,只要不和已有主键冲突即可;后续自增从表里最大值继续递增

先给字段添加主键,再开启自增长

复制代码
alter table test2 add primary key(id);
alter table test2 modify id int auto_increment;

测试插入,不指定 id,自动递增:

复制代码
insert into test2(name,sex) values('张三','男');

自定义自增长起始值

复制代码
create table test7(
    id int primary key auto_increment,
    name varchar(30) not null
) auto_increment=100;

注意:自增起始参数写在括号外面。设置完成后,第一条自动生成的数据从 100 开始。

⚠️ 坑点:自增计数器,无论插入成功还是失败,计数器都会 + 1,会出现自增断号现象。

1.8 唯一性约束 UNIQUE

主键一张表只能有一个,当业务中有多个字段都要求不能重复时,使用唯一约束。

作用:限制字段的值不能重复。

主键和唯一键的关系:

  • 主键:标识整条记录唯一性,用于检索,一张表只能一个,不允许 NULL
  • 唯一键:满足业务字段唯一性,一张表可以设置多个,允许 NULL
  • 二者是互补关系,不是对立关系,共同保证数据完整性

业务举例:学生表,id 作为主键用来检索;身份证号不能重复,设置为唯一约束。

建表示例:

复制代码
create table test8(id int primary key, name varchar(30) unique);

本例中 name 不能重复,但是可以写入 NULL。

1.9 外键约束 FOREIGN KEY

作用:维护表与表之间的引用完整性。

概念说明:

  • 主表(父表):被引用、被参考的表
  • 从表(子表):引用主表数据的表
  • 外键:从表中引用主表的字段
  • 引用键:主表上被引用的字段

外键定义在从表上;主表被引用的字段必须是主键或者唯一键。 规则:从表外键的值,要么在主表引用字段中存在,要么是 NULL。

语法:

复制代码
foreign key (从表的字段名称) references 主表名字(主表的字段名称)

业务示例:班级表为主表,学生表为从表,学生表 class_id 关联班级表 id。 新增学生的 class_id=30,前提是班级表中已经存在 id=30 的班级;想要删除班级表 id=20 的班级,要先删掉所有 class_id=20 的学生记录;class_id 可以填 NULL,代表学生暂未分配班级。

外键约束注意事项:

  1. 主表必须提前存在,或者和从表同时创建;同一张表引用自身叫做自参照表
  2. 主表被引用列必须是主键或者候选键
  3. 主键不能为 NULL,但是外键允许为 NULL
  4. 外键字段数量、数据类型,必须和主表引用字段保持一致

实操示例

复制代码
-- 定义主表:班级表
create table myclass(id int primary key, class_name varchar(20) not null);

-- 定义从表:学生表
create table myclass_stu(
    id int primary key,
    name varchar(30) not null,
    class_id int,
    foreign key(class_id) references myclass(id)
);

-- 主表插入班级数据
insert into myclass values(1,'C++'),(2,'JAVA');

-- 正常插入学生
insert into myclass_stu values(1,'Li',1),(2,'Sun',2);

-- 报错:主表不存在id=3的班级,外键拦截
insert into myclass_stu values(3,'Zhang',3);

-- class_id为NULL,允许插入,表示未分配班级
insert into myclass_stu values(3,'Zhang',null);

1.10 检查约束 CHECK

作用:校验字段的值是否符合业务条件,用来限定取值范围,拦截无效数据。

理解:NOT NULL、DEFAULT 可以看作是特殊的检查约束。

版本重点:MySQL8.0.16 之前,写 CHECK 语法不会报错,但是约束不生效,只能通过触发器实现;8.0.16 版本及以后正式支持 CHECK 约束。

示例:薪资大于 0,小于 15000

复制代码
create table test1(
    id int primary key,
    name varchar(30) not null,
    salary float,
    check(salary>0 and salary<15000)
);

insert into test1 values(1,'Li',10000.5);
-- 超出范围,插入失败
insert into test1 values(2,'Sun',17000);

1.11 删除表的约束

复制代码
-- 删除非空约束
alter table 表名 modify 列名 类型;

-- 删除唯一约束
alter table 表名 drop index 惟一约束名;

-- 删除主键约束
alter table 表名 drop primary key;

-- 删除外键约束
alter table 表名 drop foreign key 外键名;
相关推荐
一条泥憨鱼1 小时前
【Redis | 黑马点评】基于Redis的消息队列 | stream消息队列 源码解读
java·数据库·redis·缓存·lua
CodeGL1 小时前
9、Python基础之IO编程
开发语言·数据库·python
每天一道题1 小时前
Redis 缓存为什么会出问题?穿透、击穿、雪崩与缓存一致性
数据库·redis·缓存
广州工博科技1 小时前
SAP Joule for Consultants 如何为自动化与集成项目提供支持?
运维·自动化
Acrellea1 小时前
数据中心这台新变压器,让配电房的电表全失效了
运维·能源
tongyiixiaohuang1 小时前
物料成本核算单同步返回信息策略实战教程:从金蝶云星空回写 MySQL 的闭环设计
数据库·mysql
boy快快长大1 小时前
【Jenkins】入门学习
运维·学习·jenkins
星云低代码开发平台1 小时前
AI 工作台记录删除与业务审计留存:三层数据边界设计
数据库·人工智能·架构
做运维的阿瑞1 小时前
Terraform 模块组合:VPC、安全组、EC2 的输出传递与依赖编排
云原生·云计算·terraform