MySQL数据库 (六) MySQL表的约束(下),自增长约束,唯一键约束,外键约束,索引

目录

[八、auto_increment 自增长约束](#八、auto_increment 自增长约束)

九、索引

[十、unique 唯一键约束](#十、unique 唯一键约束)

唯一键和主键的区别:

唯一键和主键怎么选择?

总结:

[十一、foreign key 外键约束](#十一、foreign key 外键约束)

认识外键的必要性:

引入外键:

外键两大核心作用:

外键的本质:

十二、总结


我们接着上篇文章继续展开讲解:

八、auto_increment 自增长约束

接下来继续学习自增长约束 auto_increment,它大多配合主键一起使用,自增长约束的约束规则就是字段在插入数据时如果不手动赋值,数据库会自动取当前字段现存最大值再加 1 生成新数值。

自增有三条硬性使用规范:

  1. 字段必须是索引(日常基本绑定主键 PRI)
  2. 字段数据类型只能是整型
  3. 一张数据表里面最多只能设置一个自增字段

这也是开发里主键标配自增的原因。

举例:

我们创建表 tt21,id 字段设置 primary key auto_increment(给 id 字段同时添加主键和自增长键),name 添加非空约束。通过desc tt21查看表结构,id 字段 Extra 列标注auto_increment;使用show create table tt21查看建表语句,字段定义末尾携带 AUTO_INCREMENT 关键字,从表结构层面确认自增配置生效。

下面我们插入数据:

连续三条插入语句只填写 name 字段、不给 id 传值,分别插入 'a'、'b'、'c'。查询数据表能够看到 id 自动依次生成 1、2、3,在用户缺省主键赋值时,自增机制从默认起始值开始逐次 + 1。

继续:

我们接着主动插入一条数据 (1000,'e'),手动把 id 赋值为 1000,这条数据正常入库。此时再次尝试插入 id=1000 会触发主键重复报错;只填 name 插入新数据时,新 id 自动变成 1001,由此可以得出关键规则:自增计数器会跟随表里现存最大 id 更新,手动写入更大数值后,后续自增从这个最大值继续 + 1。

最后我们通过 show create table tt21\G 查看完整建表语句,表的末尾标注了 AUTO_INCREMENT=1002,这个数值就是数据表下一次缺省插入时将要自动生成的 id,MySQL 会自动记录并维护自增的下一个可用值,让我们在建表阶段也能够预先手动指定自增起始数字。

所以下面我们就验证一下,我们在建表阶段提前手动指定自增起始数字:

我们继续新建 表tt22,id 设置主键与自增,同时在表的末尾出添加 AUTO_INCREMENT=500,预先指定自增的起始数值。通过 show create table tt22 查看建表语句,能够在表末尾看到配置AUTO_INCREMENT=500,代表下一次缺省插入时自增从 500 起步。

下面我们开始插入数据:

接着我们连续三次插入只填写 name 字段,依次插入 'a'、'b'、'c',不手动指定 id。查询数据表后可以看到 id 自动生成 500、501、502,完全从我们预先设定的 500 开始依次自增 + 1,由此说明在建表阶段可以直接自定义自增初始值,灵活控制主键 id 的起始编号。

除此之外,MySQL 还提供 last_insert_id() 函数,用来查询当前会话里最后一条自增生成的主键值。如上我们执行 select last_insert_id();,查询结果返回 502,正好对上最后一条数据的 id 编号。这个函数在业务开发中经常使用,新增数据之后可以快速拿到刚自动生成的主键 id,用于后续关联插入其他子表数据。

所以此时,我们就可以总结出自增约束的约束规则:

  1. 第一,开启 auto_increment 的字段必须是索引字段,日常开发几乎都配合主键使用
  2. 第二,字段的数据类型只能是各类整型,字符、浮点类型无法配置自增
  3. 第三,单张数据表里面,最多只允许存在一个自增字段,不能给多个字段同时添加 auto_increment

九、索引

1. 在学习下面的唯一键约束前,我们先铺垫引出索引的基本概念,方便我们后续理解各类键约束的底层原理。我们可以把索引类比成书本的目录,书本正文就相当于数据表的数据,目录记录关键词和对应页码,索引则记录字段数值与数据存储位置的映射关系。从数据库专业定义来讲,索引是独立的物理存储结构,会针对数据表单个或多个字段的值做排序,内部保存字段值和指向原始数据位置的逻辑指针清单。

  1. 索引内部会把字段数据与对应数据行的指针有序排列,当执行条件查询时,数据库不再全表逐行扫描数据,而是先检索索引,通过匹配字段值拿到对应数据的指针,再顺着指针定位到完整行数据。依靠这套查找逻辑,能大幅度缩减检索范围,加快 SQL 查询的执行效率,这也是项目里频繁给查询字段建索引的核心原因。

  2. 索引的本质是用额外的磁盘存储空间换取查询执行时间(以空间换时间),想要提升查询速度,就需要单独开辟空间存放索引目录,对应书本目录会额外占用书页空间。结合前面学过的主键知识,主键在建表时 MySQL 会自动为主键字段生成主键索引,这也是 PRI 主键标识本质就是索引标记、自增字段必须依托索引才能生效的底层缘由,各类约束里主键和索引的绑定关系最为紧密。

十、unique 唯一键约束

前面我们知道一张数据表只能拥有一个主键,但实际开发里经常出现多个字段都需要数据唯一的场景,比如一个学生表中学号、手机号、身份证号都不能重复,这时就需要 unique 唯一键约束来补充。唯一键同样用来限制字段数据不可重复,一张表里能够创建多个唯一键,弥补主键数量受限的短板。

举例:

我们继续新建表 stu,给 id 字段添加 unique 唯一键约束 ,name 设置非空。执行 desc stu 查看表结构,被唯一键修饰的 id 字段在 Key 列标注 UNI,和主键的 PRI 标识做区分,同时该字段 Null 列是 YES,代表唯一键默认允许写入 NULL 空值,这是和主键最直观的结构区别。

下面我们插入数据:

第一条插入 ('12345','张三');再次插入 id 同样为12345 的数据,触发重复报错,印证唯一键的去重能力。 我们转而给 id 传入 NULL,(NULL,'李四') 可以正常插入,并且后续能够连续插入多条 id 为 NULL 的记录也不会冲突。核心原因是:MySQL 中 NULL 不参与唯一性校验,多条 NULL 之间不算重复。

唯一键和主键的区别:

主键与唯一键都依托索引实现唯一性约束,但规则有两处关键区分:

  1. 第一,主键字段强制非空,不能填写 NULL;唯一键字段允许为空,还能存储多条 NULL。
  2. 第二,一张表只能设置一个主键,却可以建立多个唯一键。

从业务定位区分:主键用来唯一标识单条数据,作为整行记录的身份编号;唯一键用来约束业务字段,保证手机号、证件号这类业务数据不能重复。

唯一键和主键怎么选择?

首先一个事物会体现出各种各样的属性,而我们建表就是用来表述这些属性数据,所以 MySQL 中的表就相当于 C 语言中的结构体,用来描述对象的属性。而主键为了保证事物的唯一性,我们通常选择事物众多属性中就有唯一性的字段作为主键来标识唯一性,而往往真实场景中一个事物的众多属性中有相当一部分虽然没有被选择成为主键,但这些属性依旧需要唯一性来保证。作为主键,仅仅是因为这一列被选择成为了主键,仅此而已。所以我们不能认为我们已经选择了主键就不需要唯一键了,而正是因为我们选择了主键,而其他属性也是需要维护唯一性的,因此就有了唯一键,所以主键和唯一键不冲突,反而是相互补充的。

下面我们继续通过一个例子来证明:

我们新建 student 学生表,id 字段设置为主键、name 字段非空、telphone 字段暂不添加任何约束。在建表完成后插入三条数据,id 分别是 123、124、125,主键全部不重复可以正常插入,但刘备和孙权两条记录写入了一模一样的电话号码。依靠主键只能保证学号不重复,无法管控手机号重复,最终出现了业务上不合理的脏数据,这就是缺少唯一键带来的问题。

我们接着执行查询语句 select * from student where telephone='1345678911',此时就会查到两条共用同一个手机号的数据。从业务规则来讲,一个手机号正常只绑定一名学生,想要从数据库层面杜绝同号重复录入,就需要给 telphone 字段追加 unique 唯一键,在数据入库时自动拦截重复号码。

所以主键保证记录全局唯一,用于数据的增删改查定位;唯一键用来约束业务字段的业务唯一性,一张表可以配置多个唯一键。当业务中有多个字段需要去重,选定一个做主键,剩下的全部使用唯一键约束,这是日常表结构设计的标准方案。

那下面我们就用唯一键来标识电话号的唯一性,我们先删除这个错误的表,然后再创建一个新表:

我们先执行 drop table student; 删除之前存在重复电话号的旧表,重新创建 student 表。表结构设计为:id 字段作为主键约束整条记录唯一,telphone 手机号和 QQ 号两个字段分别添加 unique key唯一约束,name 添加非空约束。执行 desc student 查看表结构,id 的 Key 列是 PRI,telphone 与 qq 字段 Key 列标注 UNI,Null 字段为 YES,代表两个唯一字段默认允许存入 NULL,也印证一张表能够设置多处唯一键。

接着插入第一条有效数据 ('123','张三','1341234','23456'),主键、手机号、QQ 均无重复。之后分步测试重复场景:第一条重复 id=123,触发主键重复报错;第二条复用已有手机号 1341234,触发 telphone 唯一键冲突;第三条复用已有 QQ23456,触发 qq 唯一键冲突。更换全新主键、全新手机号、全新 QQ ('1234','李四','1341235','23457'),三项数据全部不重复,顺利插入。此时我们就能得出结论 : 主键管控行全局唯一,多个唯一键分别管控各自字段的唯一性,三者互相配合,就是约束的体现。

唯一键默认是可以为空 null 的,但我们也可以改为非空 not null, 如下:

我们通过 alter table modify 语句,分别给 telphone、qq 字段在 unique 基础上追加 not null 非空约束。修改完成后再次 desc 查看表结构,telphone 和 qq 字段 Null 列由 YES 变为 NO,此时唯一键同时兼具唯一性 + 非空双重约束。后续插入数据时,不管是单独给 telphone 或 qq 传入 NULL,还是两个字段同时填 NULL,都会直接抛出cannot be null报错。

总结:

主键、唯一键同属唯一性约束,底层都会自动生成索引;一张表仅能一个主键,但可以设置若干个唯一键。主键天然禁止 NULL,默认自带非空;唯一键原生允许多条,如果上层要求具体业务不能为空时,我们可以手动添加 not null,变成 "唯一 + 非空",效果等价主键。

补充 :


十一、foreign key 外键约束

因为 MySQL 是关系型数据库,表和表之间难免会有关联,所以我们还要研究表和表之间的约束关系。多张数据表之间普遍存在从属关联,而外键 foreign key 就是用来约束主表、从表数据联动关系的约束。外键字段定义在从表身上,被关联的主表对应列必须配置主键或者唯一键;外键的核心校验规则:从表外键字段存入的数据,要么在主表关联字段中已经存在,要么填入 NULL,不能写入主表没有的非法编号。

外键约束更强调两点 :

    1. 表和表之间的关联,建立两张表的逻辑关联关系
    1. 表和表之间的约束,依靠约束规范双方数据

我们以班级、学生两张表举例理解:myclass 班级表作为主表,存储班级编号、班级名称,用 id 主键作为关联基准;stu 学生表是从表,存放学生 id、姓名、班级编号 class_id。现实逻辑里学生必须隶属于某个已存在的班级,学生表的 class_id 不能随便乱写,因此用 class_id 作为从表外键,关联主表 myclass 的主键 id。如果不做外键约束,有可能出现学生填写不存在的班级编号,造成数据错乱,外键刚好规避这类问题。

外键基础语法格式为 :

foreign key(从表字段) references 主表(主表关联字段)

从表依靠外键绑定主表的主键,主表的数据是从表外键取值的合法来源;主表是被依赖方、从表是依赖方,学生依附班级,对应从表 stu 依附主表 myclass,后续插入、删除数据都会遵循外键约束规则。

认识外键的必要性:

继续举例 : 我们先建立一张学生表:

我们先创建 student 学生表,id 设置无符号整型、主键 + 自增,name 和 telphone 两个字段添加非空约束,class_id 为普通 int 字段,用来关联班级编号。执行 desc student 查看表结构,id 字段 Key 为 PRI、Extra 标注 auto_increment,其余字段按约束正常生效,此时 student 表还没有配置外键。

紧接着创建 class 班级表,id 作为主键、name 非空,desc 查看后 class 的 id 字段标记 PRI,作为后续外键关联的基准主键字段。

下面我们先向班级表中插入数据:

我们现象班级表中存入有效的班级数据,我们连续插入两条班级:(1,'通信101')、(2,'通信102'),查询班级表后,表内存在 id=1、id=2 两个合法班级编号,这两个 id 就是后续学生表 class_id 能合法填入的有效值。

然后再向学生表中插入数据:

此时两张表还没有建立外键关联,我们向 student 插入学生数据:张三、李四、王五的 class_id 填 1 (对应通信 101),赵六 class_id 填 2 (对应通信 102),全部数据顺利入库。查询学生表可以看到,学生的班级编号都和主表已有的班级 id 匹配,此时一切都正常。

下面我们继续向班级表中插入学生数据:

此时我们还没有配置外键约束,我们执行插入语句 insert into student (name, telphone, class_id) values ('田七','12345982133',3),给学生的 class_id 赋值 3 表示这个学生是编号为 3 的班级。可是此时主表 class 里只有 id=1、id=2 两个班级,根本不存在编号为 3 的班级,但 SQL 依旧插入成功。查询学生表能够看到新增一条 class_id=3 的学生数据,但这条数据在业务逻辑上是无效数据,因为出现了无归属班级的学生,这就是缺少外键约束带来的数据漏洞。

紧接着我们执行 delete from class where id=1;,直接在班级主表里删掉 id=1 的通信 101 班,删除语句正常执行。删除完成后班级表只剩 id=2 的数据,但学生表里张三、李四、王五三名学生的 class_id 全是 1,对应的班级记录已经被删掉。后续查询班级关联学生时,就会出现找不到所属班级的孤立数据;如果不靠上层程序代码做校验,数据库本身无法拦截这类不合理的删除,此时就得靠业务层手动判断,就会大幅增加开发工作量。

目前我们会发现 student 和 class 这两张表只是在业务逻辑上有关联,但是数据表层面没有外键约束绑定,也就是说两张表只有关联之名、没有约束之实:

  1. 一是新增学生时,能随意写入主表不存在的班级 id;
  2. 二是主表可以随便删除被从表数据引用的主键记录,造成从表数据悬空孤立。

那我们想要从数据库底层自动规避这两类错误,就需要给从表 student 的 class_id 字段添加外键,绑定主表 class 的主键 id,由 MySQL 自动校验主从数据合法性。

引入外键:

下面我们就引入外键约束来描述两张表之间的关系:

我们先执行 drop table student; 删掉原先无外键的学生表,重新创建 student 从表。在字段定义末尾写入 foreign key(class_id) references class(id) ,以此把从表的 class_id 字段和主表 class 的主键 id 绑定外键关系。执行desc student查看表结构,class_id 字段的 Key 列显示 MUL,MUL 标识就代表该字段绑定了外键索引,外键配置正式生效;

外键约束语法:

再通过 show create table student\G 查看完整建表语句,可以看到系统自动生成 CONSTRAINT 外键名 FOREIGN KEY(class_id) REFERENCES class(id) 的完整外键定义代码,从底层确认两张表已经建立了外键约束关系。

再确认一下此时班级表中依然是两个班级,学生表中还没有学生数据。

因此下面我们就向学生表中插入数据:

插入第一条 insert into student values (100, '张三','12345',1);,id=100 无重复、手机号无重复、class_id=1 在主表存在,所有约束全部满足,数据插入成功;第二条复用主键 id=100 插入数据,触发主键重复报错,主键唯一性约束生效;第三条复用手机号 12345 插入数据,触发唯一键重复报错,telphone 的 unique 唯一约束生效;第四条更换 id=101、新手机号 12346、class_id=2,主键、唯一键、外键全部合规,顺利插入。最终学生表两条数据分别归属班级 1、班级 2,三层约束各司其职完成数据校验。

主键约束保证学生 id 全局唯一,唯一键约束保证手机号不能重复,新增的外键约束限制 class_id只能填写主表已经存在的班级编号,三层约束从不同维度规范数据,杜绝违规数据的写入。

在已经配置外键的前提下,当我们再次尝试插入 class_id=3 的学生,而主表 class 里不存在 id=3 的班级,数据库直接抛出外键约束报错。这体现外键第一条规则:从表外键字段的值,必须在主表关联主键中已存在,否则无法插入。

下来我们直接执行 delete from class where id=1 想要删掉 1 号班级,此时 student 从表里还有学生的 class_id=1 在引用这条班级数据,外键约束就会再次拦截删除操作并报错。这体现了外键的第二条规则:主表某条主键数据被从表外键引用时,不能直接删除主表这条记录,避免删完班级后,从表学生变成无归属的孤立数据。

只有我们清空了关联的学生后,主表班级表中的班级才能正常删除,继续:

我们先执行 delete from student where id=100;,删掉唯一归属 1 班的学生,查询 student 表后,已经没有任何学生的 class_id=1。再次执行delete from class where id=1;,删除 1 号班级语句执行成功,查询 class 表仅剩 id=2 的通信 102 班。所以外键默认的限制规则:想要删除主表的某条数据,必须先把从表里所有引用该主键的外键数据全部删除完毕,主表记录才可以被删除;只要从表还有引用,主表就禁止删除,依靠这个机制保证主从表数据完整性。

外键两大核心作用:

我们上面说过,外键一共承担两大核心作用:建立主从表关联、添加数据约束。

  1. 关联关系:在建表时在从表新增外键字段,该字段参照主表的主键/唯一键,在表结构层面把从表和主表绑定逻辑关联,比如学生表 class_id 参照班级表主键 id,让学生和班级天然挂钩。
  2. 约束关系:依托外键规则管控两边数据完整性,不能插入主表不存在的班级编号、不能直接删除仍在被学生引用的班级,保证学生必有对应班级、已有学生的班级不能被随意删除,以此维护主从数据完整合规。

所以外键约束 == 构建表关联,新增数据约束。

设置外键约束的初衷就是现实中绝大多数的数据天然存在关联,以班级、学生举例:班级和学生在业务逻辑上是从属绑定关系,就算不设置外键、只正常建两张数据表,字段定义齐全,但数据库不会自动识别业务关联。这种无外键场景就会出现业务脏数据:例如只开通了 100 班、101 班,却能录入归属 102 班的学生,102 班级在班级表不 存在,从而导致数据逻辑崩坏。

外键的本质:

外键的本质就是把业务数据的关联校验交给 MySQL 数据库自动管控,提前在建表阶段告知 MySQL 两张表的约束规则。后续用户执行新增、删除 SQL 时,MySQL 自动校验数据是否符合关联逻辑,违背业务规则的操作直接报错拦截,从数据库底层杜绝不合规脏数据的生成,不用再依靠上层代码手动做业务判断。

十二、总结

所以约束表示的是一种关系吗?

约束不能简单的说成 "是一种关系",约束本身分成字段自身约束和表与表之间的关联约束两大类:

  1. 单字段约束 (非空、默认值、主键、唯一、zerofill、自增) 只作用在单个字段本身,管控当前字段的数据写入规则,只限制本列数据格式,不存在和别的字段以及别的表之间的关系。 比如 name not null 非空约束,只要求 name 不能存 NULL;primary key 主键只约束本表字段值唯一、非空,全程只管控单张表内部字段,没有跨表关联。

  2. 在所有 MySQL 约束里,只有外键 foreign key 是用来表达两张表的逻辑从属关系 : 外键把从表的某个字段,绑定主表的主键/唯一键,用来描述「从表数据依附主表数据」的业务关系(学生依附班级、订单依附用户),外键约束 = 表之间的业务关系 + 数据校验规则,是唯一体现表间关系的约束。

  3. 普通字段约束 (not null/default/primary/unique/auto_increment) :只是对字段数据的写入限制,不代表关系,只约束字段本身;

  4. 外键约束 :既定义了主从表的从属业务关系,又附带数据完整性校验规则,代表表和表之间的关联关系

一句话总结:约束是一套数据校验规则,只有外键这种约束附带表之间的关系,其余约束只做字段规则限制、不含关系含义

本文详细介绍了MySQL中的三种重要约束:自增长约束(auto_increment)、唯一键约束(unique)和外键约束(foreign key)。自增长约束通常与主键配合使用,自动生成递增值,并可通过建表时指定起始值。唯一键约束用于保证字段数据的唯一性,允许NULL值且可设置多个。外键约束则用于建立表间关联,确保从表数据必须引用主表已存在的数据,维护数据完整性。这三种约束各司其职:自增长约束简化主键管理,唯一键约束保证业务数据唯一性,外键约束维护表间关系,共同构建起数据库的完整约束体系。通过实际案例演示了这些约束的具体应用场景和验证方法,为数据库设计提供了重要规范。

谢谢大家的观看!

相关推荐
无关868819 分钟前
Redis Bitmaps 用户签到系统设计方案
数据库·redis·缓存
江华森27 分钟前
FastAPI 极速开发指南 — 从零到生产级 API 实战
数据库·fastapi
左直拳1 小时前
mysql分区表自动归档
mysql·分区表·分区表归档
老纪2 小时前
Redis分布式锁进第九零篇
数据库·redis·分布式
haven-8522 小时前
MySQL事务ACID、隔离级别、MVCC、幻读解决
数据库·mysql
小高学习java2 小时前
事务的边界问题,如何判断数据回滚时机。
java·数据库·后端
迷枫7123 小时前
【无标题】
数据库
TDengine (老段)3 小时前
TDengine 扫描算子 — TableScan、TagScan 与下推优化
大数据·数据库·物联网·时序数据库·tdengine·涛思数据
放下华子我只抽RuiKe53 小时前
FastAPI 全栈后端(三):数据库与 ORM
前端·数据库·react.js·oracle·性能优化·前端框架·fastapi
BAGAE4 小时前
星链卫星数据获取:从太空安全到实时通信的技术革命
网络·数据结构·数据库·算法·云计算·hbase