mysql表的操作——mysql表的约束
文章目录
- mysql表的约束
- 表约束的基本认识
- 表的相关约束
- 空属性
- 默认值
- 列描述
- zerofill
- 主键
- 自增长
- 唯一键
- 外键
mysql表的约束
本篇文章,我们将着重认识,在mysql中对于表的约束条件!
其实在讲mysql的数据类型的时候,就已经简单说过:
👉数据类型,本质上也是mysql对于表的约束!
表约束的基本认识
首先,我们还是需要对这个表的约束做一个基本的认识!
表的约束:
表中一定会有各种的约束:如数据类型,数据类型是否越界等…
基本思想:
mysql通过这些约束,能够倒逼程序员,要按照约束条件进行正确地数据插入!
在mysql看来:通过约束就能够保证,插入的数据符合约束条件,是合法的!
约束的最终目标 👉 保证插入的数据完整性和可预期性!
也就是说,从表中获取的数据,起码是可以预期到的!而不是不符合约束条件的值。
但是:还是要说的是 -> 约束条件只能保证数据的合法性与可预期性,但是无法保证正确性!
表的相关约束
对于表的约束,其实有很多:
null/not null,default, comment, zerofill,primary key,auto_increment,unique key…
我们这里挑选几个比较重要的来进行讲解!
空属性
在正式讲解空属性前,我们需要明白:
null
和''
是不一样的!前者表示这一项为空(没有该项),后者表示有,但是内容是空串!
本质上是因为:在mysql中,null是不参与计算的:
mysql> select null-> ;
+------+
| NULL |
+------+
| NULL |
+------+
1 row in set (0.00 sec)mysql> select null + 1;
+----------+
| null + 1 |
+----------+
| NULL |
+----------+
1 row in set (0.00 sec)
我们现在先创建一个表:(在id面带上not null,name后面带上null)
我们使用show create table t1
来进行查看表的创建相关属性:
然后尝试插入一些数据:
我们发现:
全列插入是可行的,单纯插入名字也是可行的,但唯独单独插入id不行!
本质的原因是什么呢?
其实是因为,我们在建表的时候,对于id这一列的数据,设置了约束not null!
mysql> desc t1;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int | NO | | NULL | |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
我们通过查看表的结构可以发现,对于id字段,是不允许为空的!
而如果我们不显示指定数据是否允许为空,mysql自动默认设置可以为空!
这就是mysql对于表的一种约束!要求某种数据不能为空!又或是默认为空!
如果设置了不为空,那么我们在获取该数据的时候,至少不会取出来为空的数据!
空属性其实很简单:
如果要设置不为空属性,只需要在数据的类型后面追加声明not null!
如果声明了null,或者说不声明,mysql的处理都是允许该字段为空。
默认值
默认值:关键字为default。
我们对于默认值肯定是很熟悉的!比如c++内函数参数的缺省参数!其实也叫默认值。
在mysql中,是允许我们对于字段设置一个默认值的!
只不过,当我们不写任何默认值的时候,mysql的处理是defalut null:
也就是,默认值mysql都是处理为null的!
只不过如果设置了not null
:
就没有mysql自动处理的默认值!所以这就是为什么不能忽略该字段的数据的插入!
下面,我们就创建一个新的表,来测试一下默认值的属性:
mysql> create table stu(-> id int not null,-> gender char(2) default '男',-> telphone char(11) not null default '00000000000',-> class varchar(20) -> );
Query OK, 0 rows affected (0.02 sec)mysql> desc stu;
+----------+-------------+------+-----+-------------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+-------------+-------+
| id | int | NO | | NULL | |
| gender | char(2) | YES | | 男 | |
| telphone | char(11) | NO | | 00000000000 | |
| class | varchar(20) | YES | | NULL | |
+----------+-------------+------+-----+-------------+-------+
4 rows in set (0.01 sec)
我们尝试插入一些数据进行测试:
我们来简单总结一下对于默认值的约束属性:
1.如果设置了默认值,mysql会采用用户输入的!反之default null
2.如果default和not null同时出现:
- 因为not null -> mysql不会自动设置default null,所以该字段非空
- 但因为用户显式指定了默认值,mysql使用了用户提供的默认值
- 最终就导致,这一列是可以忽略插入的,mysql会使用默认值!
总结就是:只有设置了default的字段,才能忽略插入!
而系统默认自带默认值为空,所以默认允许我们忽略字段的插入!
列描述
列描述其实就是comment:
它其实严格意义上来说,只有约束的名义,没有实际约束的操作!
它就很像是我们在写代码的时候的一些注释!
它并不会通过实际的一些操作来对表进行约束,它更多的作用是给数据库的使用者,或是DBA(database administrator),,数据库管理员进行了解数据用途的!
下面我们简单展示一下即可:
mysql> create table t2( id int not null comment '这是id', name varchar(20) default 'zhangsan' comment '这是姓名' );
Query OK, 0 rows affected (0.03 sec)mysql> desc t2;
+-------+-------------+------+-----+----------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+----------+-------+
| id | int | NO | | NULL | |
| name | varchar(20) | YES | | zhangsan | |
+-------+-------------+------+-----+----------+-------+
2 rows in set (0.00 sec)mysql> show create table t2;
| t2 | CREATE TABLE `t2` (`id` int NOT NULL COMMENT '这是id',`name` varchar(20) COLLATE utf8mb4_general_ci DEFAULT 'zhangsan' COMMENT '这是姓名'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci |
其实这个列描述根本就没有加入到表的结构内!
我们只能通过show create table table_name
语句来查询到相关的列描述!
所以,这就是为什么上面说:
列描述只有约束的名义,但是没有约束的动作!
因为它的本质就是像注释,但在合理的情况下确实也起到了约束的作用!
zerofill
在讲解数据类型的时候,我们遗留了一个问题:
对于浮点数来说,或者字符串来说,声明类型的时候可以通过
类型()
语句:
通过括号内的设置来决定类型的大小或长短!
但是,我们可以发现,对于int来说,有时候也能够发现后序会有如int(11)
的情况!
但是要说明的是:在mysql 8.0.17以上的版本是没有这个情况出现的!
我的mysql版本就是比较高的,所以我这里是显示不出来的!
ynp@hcss-ecs-1643:~$ mysql --version
mysql Ver 8.0.43 for Linux on x86_64 (MySQL Community Server - GPL)
在旧版本下:
如果直接使用int,那么表中显示的是int(11)
如果使用int unsigned,那么表中显示的是int unsigned(10)
现在我们就基于旧版本的情况,来了解一下,对于整数类型括号内的数字是干什么的?
目前我们至少能够知道:它肯定不是指示大小的!
mysql> create table t3( a int(5) zerofill, b int(6) unsigned);
Query OK, 0 rows affected, 3 warnings (0.02 sec)mysql> desc t3;
+-------+--------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------------------+------+-----+---------+-------+
| a | int(5) unsigned zerofill | YES | | NULL | |
| b | int unsigned | YES | | NULL | |
+-------+--------------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
我们先看现象,最后来解释是什么:
mysql> insert into t3(a, b) values (115, 12345678)-> ;
Query OK, 1 row affected (0.00 sec)mysql> insert into t3(a, b) values (1115, 12345678);
Query OK, 1 row affected (0.01 sec)mysql> insert into t3(a, b) values (11151, 12345678);
Query OK, 1 row affected (0.00 sec)mysql> insert into t3(a, b) values (11131251, 12345678);
Query OK, 1 row affected (0.01 sec)mysql> select * from t3;
+----------+----------+
| a | b |
+----------+----------+
| 00115 | 12345678 |
| 01115 | 12345678 |
| 11151 | 12345678 |
| 11131251 | 12345678 |
+----------+----------+
4 rows in set (0.00 sec)
我们可以发现,当我们对a的字段显示设置括号内的数字,并且使用zerofill对其约束:
读取数据的时候,a的长度是必须满足五位的(不足高位补0,足够正常显示!)
其实zerofill,从名字上来看:就是填充0!
作用:使指定列的数字显示长度至少达到指定的长度!超出则不管。
一些细节部分:
- MySQL 8.0+ 的优化行为:
如果不需要 ZEROFILL,可以忽略显示宽度,它不影响实际数据存储。- zerofill字段只能给整数使用!
该字段就是对数据进行格式化控制的!只有显示的时候是这么长,筛选,使用的时候不是:
mysql> select * from t3;
+----------+----------+
| a | b |
+----------+----------+
| 00115 | 12345678 |
| 01115 | 12345678 |
| 11151 | 12345678 |
| 11131251 | 12345678 |
+----------+----------+
4 rows in set (0.00 sec)mysql> select * from t3 where a=115;
+-------+----------+
| a | b |
+-------+----------+
| 00115 | 12345678 |
+-------+----------+
1 row in set (0.00 sec)mysql> select hex(a) from t3;
+--------+
| hex(a) |
+--------+
| 73 |
| 45B |
| 2B8F |
| A9D973 |
+--------+
4 rows in set (0.00 sec)
我们可以很清晰的发现,不管是使用where筛选,还是使用hex函数显示十六进制:
我们都是不需要使用指定的长度的数字来进行操作的!使用原数字即可!
(内部存储是正常的,只不过因为zerofill的约束,使得显示的时候做格式化控制。)
但是现在还需要解决最后一个细节:
即为什么在mysql 8.0以下的版本:默认int(11),int(10) unsigned
呢?
我们根据zerofill的约束理解,我们知道该字段就是用来填充格式的!(不足高位补0)
int存储范围:-2,147,483,648 到 2,147,483,647(即 -2³¹ ~ 2³¹-1)
unsigned int存储范围:0到 4,294,967,295(即 0 ~ 2³²-1)
我们发现,不管是int还是unsigned int,数据最长也就是10位数!
但是对于int来说,需要多一位来表示符号位! -> 所以int的默认显示宽度为11!
主键
主键:primary key用来唯一地约束该字段里面的数据,不能重复/为空,一张表中最多只能有一个
主键;主键所在的列通常是整数类型。
首先我们需要弄明白:主键能用来做什么?
我们学过哈希表,我们管unordered_map内存储的内容是键值对(key, value)!
主键其实作用就和哈希中的Key是类似的,就是一个索引的基准值!(在一张表内不能重复!)
需要使用主键的时候,只需要在建表的时候声明以下primary key即可!
mysql> create table t4 (id int primary key, name varchar(20));
Query OK, 0 rows affected (0.03 sec)mysql> desc t4;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int | YES | PRI | NULL | |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
然后查询表的结构,可以发现,被设置主键的字段,Key的位置会带上PRI
标志!
查询表的创建:
mysql> show create table t4 \G;
*************************** 1. row ***************************Table: t4
Create Table: CREATE TABLE `t4` (`id` int NOT NULL,`name` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
1 row in set (0.00 sec)
我们发现,mysql确实设置了对应的主键!PRIMARY KEY ('id')
!且不会在变量后面跟着!
最重要的是,一旦设置主键,mysql自动为其补上not null!
下面尝试插入一些数据:
我们发现,一旦设置了主键:
就不能忽略插入,也不能插入null,更不能重复插入了!
因为主键的功能:就是要保证该字段的内容,在表内是具有唯一性质的!
当然,主键是可以后续追加或者删除的!
删除主键:alter table table_name drop primary key
因为mysql知道主键是谁,所以直接说删除主键即可,不需要指明主键是哪一列。
添加主键:alter table table_name add primary key(字段列表)
前面说:一张表只能有一个主键,但是不代表逐渐内只有一列!
如果说,我们今天并不是想以某个单一的字段作为主键,也是可以声明复合键的:
mysql> create table person(-> uid int,-> name varchar(20),-> tel varchar(11),-> job char(10),-> primary key(uid, name)-> );
Query OK, 0 rows affected (0.03 sec)mysql> desc person;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| uid | int | NO | PRI | NULL | |
| name | varchar(20) | NO | PRI | NULL | |
| tel | varchar(11) | YES | | NULL | |
| job | char(10) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)mysql> show create table person \G;
*************************** 1. row ***************************Table: person
Create Table: CREATE TABLE `person` (`uid` int NOT NULL,`name` varchar(20) COLLATE utf8mb4_general_ci NOT NULL,`tel` varchar(11) COLLATE utf8mb4_general_ci DEFAULT NULL,`job` char(10) COLLATE utf8mb4_general_ci DEFAULT NULL,PRIMARY KEY (`uid`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
1 row in set (0.00 sec)
我们发现:确实是可以以多列合并作为一个主键的!
只不过声明方式是需要另起一行,说明复合主键由哪些字段构成!
经过一番数据插入的测试,对于复合主键可以得到以下结论:
1.复合主键中的多个字段,每一个都不能为空!
2.复合主键的唯一性是指:对复合主键内的字段全都相同!只要有一个不同就是不同的!
这其实也就是一个主键!只不过这个主键是以多个字段的情况来进行判断的!
其实也是对表做了一定的约束!要求插入的数据,在(id, name)这两个字段上不能完全重复!
自增长
auto_increment:当对应的字段,不给值,会自动的被系统触发,系统会从当前字段中已经有的最大值+1操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。
注意:auto_increment只能是整数字段才能使用!且auto_increment和default不能同时设置!
(因为auto_increment是要给对应字段设置一个值的,default也是,二者存在冲突!)
mysql> create table t5(id int not null default 1 auto_increment, name varchar(20) );
ERROR 1067 (42000): Invalid default value for 'id'
还有就是:如果要自增长,它必须是一个键!(可以不是主键!后序会说)
(之不过一般来说,auto_increment是配合着primary key使用的!)
mysql> create table t5(id int not null auto_increment, name varchar(20) );
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
建立一个带有自增长字段的表:
mysql> create table t5(id int not null primary key auto_increment, name varchar(20) );
Query OK, 0 rows affected (0.03 sec)mysql> desc t5;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)mysql> show create table t5 \G;
*************************** 1. row ***************************Table: t5
Create Table: CREATE TABLE `t5` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
1 row in set (0.01 sec)
我们尝试插入一些值:
第一次的值一旦我们指定好后(也可以不插入该字段,让mysql自行指定默认值):‘
下一次开始mysql自动会记录AUTO_INCREMENT的值:即下一次插入该字段的默认值!
如果我们再随便插入一个数据:
我们发现,下一次的AUTO_INCREMENT = 6,也就是说:
AUTO_INCREMENT是按照当前表中:该字段的最大值+1进行设置的!
唯一键
在mysql中,是不可能只有一个字段是需要作为唯一值的。
就拿我们人来说:身份证是唯一值,但是我们的电话号码也是唯一值啊!
但是,mysql又规定,一张表中只能有一个主键!这该怎么办呢?
注意:这里绝对不能使用复合主键!
我们这里要求的是:两个字段都需要唯一!但是复合主键只要有一个字段不一样就可以插入!
所以,基于此要求:
mysql提供了另外一种键!即唯一键:unique(简写)!
mysql> create table person(-> id int primary key,-> name varchar(20),-> tel char(11) unique,-> job char(10)-> );
Query OK, 0 rows affected (0.03 sec)mysql> desc person;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| name | varchar(20) | YES | | NULL | |
| tel | char(11) | YES | UNI | NULL | |
| job | char(10) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
我们可以发现,设置了唯一键的字段,在Key状态下显示UNI!
尝试插入一些数据进行测试:
我们发现,其实唯一键和主键的用法类似!只不过说在于:
当唯一键字段有默认值(甚至为空)时,是可以进行忽略该字段的插入的!这点主键不行。
其余的用法差不多:也是在表中只能出现一次的!即该字段不能出现重复的值!
现在我们就需要理解:
主键和唯一键的区别是什么?它们仅仅是在插入值能否为空上有一些区别!
我们可以简单理解成,主键更多的是标识唯一性的。而唯一键更多的是保证在业务上,不要和别的信息出现重复。乍一听好像没啥区别,我们举一个例子
其实我们给唯一键设置上not null,其实和主键就在使用上就没有区别了!
主键大部分情况下,是选择一些和业务无关的字段!以便后序进行快速搜索的。
外键
外键用于定义主表和从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为null。
mysql是一种关系型数据库,数据库和数据库之间是会存在一些关系的:
比如:
现在我们有一个表,专门描述的是学校内班级编号和班级名称的关系!
然后,我们需要管理学生(学号、姓名、班级…)
我们当然可以一股脑地把所有信息塞到一张表:
但是,如果这样做,会导致表内一些列项会异常的冗余(入班级编号、名称)
所以,此时我们可以这样做:
我们让一张表记录学生的姓名、学号,和班级的id。另一张表记录班级名称和id的映射关系!
这样子,两张表在stu->class_id和班级表myclass->id是形成了约束关系的!
(此时很明显,stu表是依附于myclass表的!)
我们规定:
当stu内尝试插入新数据时,需判断class_id是否已存在于班级表?(业务上不允许不存在的班级出现)
当myclass删除数据时,需判断stu内是否还存有该字段的数据?如果有是不能删除的!
但是,仅仅是创建这两张表也是没有用的!因为只有约束之名,没有约束之实!
还必须要通过一定的技术手段来实现,两个表之间的约束!
创建外键的语法:
foreign key(该表内的列) references 主表(主表的列)
mysql> create table myclass(-> id int primary key,-> name varchar(20)-> );
Query OK, 0 rows affected (0.03 sec)mysql> create table student(-> id int primary key,-> name varchar(20),-> class_id int,-> foreign key(class_id) references myclass(id)-> );
Query OK, 0 rows affected (0.03 sec)mysql> desc myclass;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)mysql> desc student;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| name | varchar(20) | YES | | NULL | |
| class_id | int | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
现在我们先向班级表中分别添加一些数据:
mysql> insert into myclass values (1, '高三一班');
Query OK, 1 row affected (0.01 sec)mysql> insert into myclass values (2, '高三二班');
Query OK, 1 row affected (0.01 sec)
我们尝试插入不存在主表的数据,发现是插入失败的!
我们尝试删除主表中的一些数据:
我们发现:想要删除主表的数据,比如得保证该数据在从表中是没有的!否则无法删除!
所以,最后我们就知道了,在mysql中如何使用外键来对两个表进行约束!