【MySQL】表的约束(下)

文章目录

  • [1. 主键约束(primary key)](#1. 主键约束(primary key))
    • [1.1 主键的理解和案例练习](#1.1 主键的理解和案例练习)
    • [1.2 添加和删除主键的方法](#1.2 添加和删除主键的方法)
    • [1.3 复合主键](#1.3 复合主键)
  • [2. 自增长(auto_increment)](#2. 自增长(auto_increment))
  • [3. 唯一键约束(unique)](#3. 唯一键约束(unique))
    • [3.1 语法和基本使用](#3.1 语法和基本使用)
    • [3.2 理解和主键的区别以及如何选择](#3.2 理解和主键的区别以及如何选择)
  • [4. 外键约束(foreign key)](#4. 外键约束(foreign key))
    • [4.1 不使用外键](#4.1 不使用外键)
    • [4.2 存在的问题](#4.2 存在的问题)
    • [4.3 外键的定义和语法](#4.3 外键的定义和语法)
    • [4.4 添加外键后的效果](#4.4 添加外键后的效果)

1. 主键约束(primary key)

1.1 主键的理解和案例练习

主键:primary key用来唯一的约束对应字段里面的数据,不能重复,不能为空,一张表中最多只能有一个主键 ;主键所在的列通常是整数类型。
就是我想让某列的数据不重复,每一个都是唯一的,而且不能为空,那我就可以给这一列添加主键约束。
比如我们在学校,在班级中都有一个学号,这个学号一定是每个同学都有的,不可能为空,并且每个人的学号都是唯一的,不重复的。一个学号就可以唯一的标识一个同学。

案例:

建表
create table t16( id int unsigned primary key comment '学号', name varchar(20) not null );

id这一列我们设为主键,id列我们并没有约束not null,但是我们desc能看到,这一列NULL这个属性是NO,几不允许为空(name列是我们手动加的)
同时show create table可以看到

MySQL保存的建表语句是这样的

那id这一列具有主键约束,所以就不能为空,且不能重复,我们来插入数据试一下:


主键列不能重复,不能为空
那未来我想对某条记录增删查改,通过主键列的值一定能唯一的找到这一条记录

1.2 添加和删除主键的方法

添加主键第一种方法就是我们上面使用的:

建表的时候,给对应列添加primary key,指定该列为主键列

下面介绍第二种方法:建好表之后,再添加主键

sql 复制代码
alter table t16 add primary key(列名);

演示一下,先把上面表中的主键去掉
所以,怎么去掉主键
alter table t16 drop primary key;
修改表,怎么修改?去掉主键(drop primary key)

此时id就不是主键了(但是依然不能为空)
那此时id是不是就可以重复了

是的。
那现在我想对这张已经建立好的表添加主键,怎么做呢?
还是修改表,怎么修改?添加主键(指定你要添加的列)
alter table t16 add primary key(id);

报错了,为什么?
因为现在id这一列有重复值。
所以,简单说一下:
我们要么在建表的时候就指定好对应的主键,要么表建好之后,没插数据之前,赶紧把主键指定好。不要已经插入了一些数据之后,你再去添加主键。
要不然就可能出现上面的情况,那你可能就得把重复的数据删掉了,就不好弄了。(我们这里是自己做练习,实际开发中你敢随便删除人家插入的数据嘛)
那我们这里就随便删除一条数据
delete from t16 where name='鲁智深';

然后就可以添加成功了

总结一下:

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

sql 复制代码
alter table 表名 add primary key(列名)

删除主键

sql 复制代码
alter table 表名 drop primary key;

1.3 复合主键

上面提到,一个表中只能有一个主键,但是:

这不意味着主键只能添加给表中的一列!
可以指定多个字段同时作为主键------复合主键。

案例:建一个选课表,有三列:学号、课程代码和该门课的成绩。然后我想让学号和所选课程的课程代码共同组成复合主键,那么这两列的信息组合起来就不能重复,不能为空

建表
create table pick_course( id int unsigned, course_id int unsigned , score tinyint unsigned, primary key (id,course_id) );

插入数据
首先,不同的学生选同一门课程是可以的

主键没有出现重复
同一个同学选了多门不同的课也是可以的

没有主键冲突
但如果一个同学想多次选同一门课,则不被允许

发生了复合主键(100-340)冲突,不被允许

2. 自增长(auto_increment)

auto_increment:当对应的字段被设置了auto_increment属性,插入时若忽略该列,则会被系统自动触发,MySQL 会自动为其生成一个唯一的递增值 并插入。
通常和主键搭配使用,作为逻辑主键。
自增长的特点:
任何一个字段要做自增长,前提是本身是一个索引(key一栏有值,什么是索引,后面会讲)
自增长字段必须是整数
一张表最多只能有一个自增长

案例:

建表:
create table t17( id int unsigned primary key auto_increment, name varchar(20) not null );

设置id这一列为主键,并且带上auto_increment自增属性
那如果插入数据的时候忽略id这一列

我们发现id的值自动插入了1,2,3,4
即默认从1开始,后续每次自增1
那如果后续插入我又自己显式指定一个值,比如id为1000

那就插入我们自己指定的值,且后续插入若继续忽略该列,它就从1000的基础上自增1

那它如何做到这一点呢?


我们发现,除了给这一列设置了auto_increment属性之外,最后一行还有个AUTO_INCREMENT=1002 。
这个就是MySQL设置的自增计数器 ,下次插入如果我们进行了忽略,它默认就从这个值的基础上自增1。
我们在建表的时候也可以手动设置这个起始值。

建好表后,我们也可以修改
ALTER TABLE t17 AUTO_INCREMENT = 2000;
这样下次插入就从2000开始

并且每次生成的自增值是唯一的,如果可能出现重复的时候,他会自动跳过

然后介绍一个函数last_insert_id(),可以获取上次插入的 AUTO_INCREMENT 的值(批量插入获取的是第一个值)

当然,不是说列名必须是id,LAST_INSERT_ID() 关注的是 AUTO_INCREMENT 对应的列,与列名完全无关!

索引:

在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。
索引的作用相当于图书的目录 ,可以根据目录中的页码快速找到所需的内容。(空间换时间)

索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。

数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息。

(这里先简单了解一下,后面我们有专门的章节来讲)

3. 唯一键约束(unique)

3.1 语法和基本使用

一张表中有时候可能有很多字段需要唯一性,即数据不能重复,但是一张表中只能有一个主键,唯一键就可以解决表中有多个字段需要唯一性约束的问题
唯一键允许为空,而且可以多个为空,空字段不做唯一性比较。

关于唯一键和主键的区别:

我们可以简单理解成,主键更多的是标识唯一性的(主键不能重复且绝对不允许 NULL 值,一张表只能有一个主键 )。而唯一键更多的是保证在业务上,不要和别的信息出现重复(唯一键不允许重复,但允许为空,且允许有多个 NULL 值,如果你想不允许为空,就需要再手动加上not null约束。一张表可以设置多个唯一键)。

举一个例子:

假设一个场景:

比如在公司,我们需要一个员工管理系统,系统中有一个员工表,员工表中有两列信息,一个身份证号码,一个是员工工号,我们可以选择身份号码作为主键。

而我们设计员工工号的时候,需要一种约束:而所有的员工工号都不能重复。具体指的是在公司的业务中不能重复,我们设计表的时候,需要这个约束,那么就可以将员工工号设计成为唯一键。

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

下面我们具体建个表来看看

create table stu( id char(20) unique, name varchar(32) not null );

建一个学生表,有两列,id和名字,其中id我们设置了唯一键约束(那就不能重复,但允许为空且允许多个为空)
来插入数据试一下

没毛病,和我们上面说的是一致的。

3.2 理解和主键的区别以及如何选择

那主键和唯一键的区别就是主键不能为空,而唯一键允许为空。不过它们都具有唯一性的约束,那我该怎么进行选择呢?

🆗,如何理解呢?
我们每一个人身上,都具有很多的属性,比如姓名,年龄,身份证号,手机号等等。而我们建的表,通常就是描述了一个类似这样一个对象,比如学生表、员工表;表中的每一列通常就对应该对象的一个属性,比如年龄,手机号,姓名;后续每一列填充的值就是一个对象的该属性的值。
那我们选择某一列作为主键列的时候,可以用两者选法。第一种就是从众多 不允许重复的属性中随便选一个,第二种,即上面例子中提到的,通常选一个与业务无关的字段设为主键。
这里的"众多"二字说明了一个问题,即一个对象的属性中,可能有很多属性需要保证唯一性(比如身份证号、学号、电话号);换句话说,即使有的列没有没选做主键,但是仍然有保证其唯一性的需求。
所以,主键和唯一键虽然都可以进行唯一性的约束,但是两者并不冲突,而是互补的。

下面来看这样一个场景:假设现在有一个辅导员,建个一个学生表,要录入班级学生的信息


表中有3列,学号(id),姓名,电话。按理说学号和电话都应该保证唯一性,名字可能有重名的情况。
那我们这里选择id作为主键(那就不能为空,每位同学都必须有学号且不能重复),所以其实可以把电话加上唯一键约束(不重复,但可以为空,比如大一刚开学,有的同学新买了手机,还没办卡)。但是我们先不加
然后插入一些数据,假设呢辅导员插入信息的时候图省事而且比较粗心,第一条数据插入之后,直接复制上一条SQL语句,然后修改一下

如果学号忘了修改,没关系,直接报错了。但是如果电话号忘了修改,直接填上去,也没允许了,因为我们没有设置唯一键约束。
这显然是不合理的,因为两个同学电话号一模一样。
如果未来某个同学给你打电话了反映了一些情况,然后直接挂了,你想找一下是谁?

但这样你怎么能找到呢?到底是谁?怎么这么多人都是这个号码!
那这就是你当初设计数据库的时候,有bug!

所以,再回到我们上面说的:

主键这一列当然保证了唯一性,但是不否认,你可能还有其它的列也需要保证唯一性,这时,你就可以把这些列加上唯一键约束。
而上面为什么选择学号作为主键呢?我们的学号,大学四年一般不会变(而且学号肯定不为空,每个同学都有),除非你转专业了可能要变。而电话号码变化的可能性会相对大一点,而且在学校的日常生活用到电话号码的场景也可能更多一点。
这就是我们上面说的建议将主键设计成为和当前业务无关的字段,这样,当业务调整的时候,我们可以尽量不会对主键做过大的调整。然后此时如果还有其它需要保证唯一性的字段,就可以用唯一键来约束。

id作为主键,电话号和QQ号作为唯一键

这样你如果电话或QQ号忘了修改直接就报错了。
当然这肯定不能完全避免你录人信息的时候出现错误,但是起到了约束你这一列数据至少不会出现重复的情况。

相信现在大家就能更好地理解主键和唯一键的区别以及如何选择它们了。

4. 外键约束(foreign key)

外键呢第一次学其实不是很好理解,所以下面我们先给出一个例子,通过例子慢慢理解外键,并给出外键的定义。

来看这样一个场景:

4.1 不使用外键

现在有两张表:


学生表和班级表,那我们很容易发现这两张表其实是有关系的 。班级表里面记录了所有的班级信息(班级id和班级名),学生表包括学号(Id)、姓名以及该学生所属的班级id即class_id。
学生表中class_id和班级表的id肯定是一 一对应的。通过class_id,就能找到这名学生属于哪个班级。
我们把班级表叫做主表,学生表叫做从表。即学生从属于班级,一个特定的学生一定属性某个特定的班级。

然后我们来建立这两张表:

学生表
create table student( id int unsigned primary key auto_increment, name varchar(20) not null, class_id int );

class_id这一列我们没有添加任何约束
然后来建班级表

然后我们来插入一些班级,有了班级里面就可以有学生了

然后我们来向学生表中插入一些学生到指定的班级中
学生表中id我们设置了auto_increment,所以我们可以忽略,使用自动递增填充的数据

在学生表中,我们虽然没有存储具体的班级名称,但是,未来我通过班级id就可以找到学生所属的班级
比如,我要查找宋江是哪个班的?

目前这些操作都没啥问题

下面我再来做一些操作:

目前两张表是这样的

只有两个班级,里面分别有一些学生。
现在又来一名学生

插入到了id为3的班级中,这里插入成功。
有问题嘛?
虽然这两张表存在一定的关系,但是,仅仅是表面存在一些关系罢了。并没有实质的约束关系。
此时两张表在业务上是有相关性的,但是在业务上没有建立约束关系,那么就可能出现问题。

所以,虽然只有两个班级,但是现在我把一个学生插入到了第三个班级表中没有的班级。但我依然能插入进去,因为我插入的是student表,管你class表什么事啊!

但是啊,这不合理,这不合逻辑!

4.2 存在的问题

设想一下:

大数据专业,现在只有两个班

但是却出现了这样的情况:一个学生属于一个不存在的班级?

我们拿着它的班级id去查它的班级,却发现查到了空!
这显然是不合理的,你既然是这个专业的学生,一定属于这个专业的某个班级啊。

然后呢再看一个场景:


大数据2班现在有3名学生。
现在我想把大数据2班干掉!

可以干掉。
但是!和上面一样,这不合理啊。你问这三个同学,你是哪班的?
它们说我是大数据2班的,结果呢?
根本没有大数据2班啊!
你这个班级里面还有学生呢,但是却可以直接把班级干没了,这当然不合理。

做这些操作是没问题的,但是它不合理。那有没有什么办法可以避免这种在有关系的两张表中做这样不合理的操作呢?

外键就可以登场了。

4.3 外键的定义和语法

定义:

外键用于定义主表(被引用表)和从表(包含外键的表)之间的关系,外键约束主要定义在从表上,主表被引用的列则必须含有主键约束或unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为null(因为唯一键也可以被引用,唯一键约束时可以插入null)。
外键的主要作用是约束数据的一致性、完整性和关联性。

通俗理解:外键就像是一个指针,指向另一个表中的记录。它确保了我们只能插入或更新为在引用表中存在的值,或者将引用表中的相应记录删除或更新时,对外键表做相应的处理(如阻止删除、级联删除等)。

一句话总结:

外键就是一张表对另一张表的"引用承诺":我引用的数据,在你那里必须真实存在,并且当对两个表进行一些不合理的操作时,这种约束会有能力阻止你进行相应的操作。

语法:

sql 复制代码
foreign key (从表列名) references 主表(列名)

4.4 添加外键后的效果

解决方案就是通过外键完成的。
建立外键的本质其实就是把相关性交给mysql去审核了,提前告诉mysql表之间的约束关系,那么当用户插入不符合业务逻辑的数据的时候,mysql不允许你插入。

我们来试一试:

首先来简单分析一下:

外键用于定义主表(被引用表)和从表(包含外键的表)之间的关系,外键约束主要定义在从表上,主表被引用的列则必须含有主键约束或unique约束。
那在我们上面的例子中,班级表是主表(被引用表),学生表是从表。
所以要使用外键,就是学生表中的class_id引用班级表的id列,班级表中的id是被引用的列(那就要是主键或唯一键,我们上面的例子中班级表中id是主键):
foreign key(class_id) reference class(id)

那下面来给这两张表建立真正的外键约束:

包含外键的表是从表------学生表,所以删掉原来的学生表,我们重新建表

学生表中的class_id作为外键列,引用的列是班级表中的id列
然后我们先来插入一些数据

然后再来做上面那两种不合理的操作:

插入一个学生,指明它的班级id为3,并不存在对应班级

我们发现,外键约束就起作用了,就不能插入了

同样地,两个班级现在都有学生,如果你想干掉班级

也不行。
除非你先把班级里面的学生清空,班级是空的,你才可以删


其中 CONSTRAINT student_ibfk_1 的作用就是给外键约束起的名字为student_ibfk_1 。这是默认自动生成的约束名,我们建表是也可以自己起名字,同样的语法

如上,就是对外键约束的理解。

相关推荐
dawudayudaxue2 小时前
sqlite在安卓下使用ndk的交叉编译
android·数据库·sqlite
lkbhua莱克瓦242 小时前
Apache Maven全面解析
java·数据库·笔记·maven·apache
optimistic_chen2 小时前
【Redis系列】哨兵模式
linux·数据库·redis·分布式·哨兵
爱编码的傅同学2 小时前
【线程的同步与互斥】初识互斥量与锁
android·java·开发语言
_李小白2 小时前
【Android 美颜相机】第十天:YUV420SP和RGB
android·数码相机
2501_944526422 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 收藏功能实现
android·java·开发语言·javascript·python·flutter·游戏
2501_944526422 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 个人中心实现
android·java·javascript·python·flutter·游戏
啊吧怪不啊吧2 小时前
极致性能的服务器Redis之Hash类型及相关指令介绍
大数据·数据库·redis·sql·mybatis·哈希算法
Jomurphys2 小时前
Kotlin - 引用操作符 ::
android·kotlin