mysql表的操作——mysql表的约束

文章目录

mysql表的约束

本篇文章,我们将着重认识,在mysql中对于表的约束条件!

其实在讲mysql的数据类型的时候,就已经简单说过:

👉数据类型,本质上也是mysql对于表的约束!

表约束的基本认识

首先,我们还是需要对这个表的约束做一个基本的认识!

表的约束:

表中一定会有各种的约束:如数据类型,数据类型是否越界等...


基本思想:
mysql通过这些约束,能够倒逼程序员,要按照约束条件进行正确地数据插入!

在mysql看来:通过约束就能够保证,插入的数据符合约束条件,是合法的!

约束的最终目标 👉 保证插入的数据完整性和可预期性!

也就是说,从表中获取的数据,起码是可以预期到的!而不是不符合约束条件的值。

但是:还是要说的是 -> 约束条件只能保证数据的合法性与可预期性,但是无法保证正确性!

表的相关约束

对于表的约束,其实有很多:

null/not null,default, comment, zerofill,primary key,auto_increment,unique key...

我们这里挑选几个比较重要的来进行讲解!

空属性

在正式讲解空属性前,我们需要明白:

null''是不一样的!前者表示这一项为空(没有该项),后者表示有,但是内容是空串!

本质上是因为:在mysql中,null是不参与计算的:

sql 复制代码
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

sql 复制代码
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自动处理的默认值!所以这就是为什么不能忽略该字段的数据的插入!

下面,我们就创建一个新的表,来测试一下默认值的属性:

sql 复制代码
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),,数据库管理员进行了解数据用途的!

下面我们简单展示一下即可:

sql 复制代码
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版本就是比较高的,所以我这里是显示不出来的!

sql 复制代码
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)


现在我们就基于旧版本的情况,来了解一下,对于整数类型括号内的数字是干什么的?

目前我们至少能够知道:它肯定不是指示大小的!

sql 复制代码
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)

我们先看现象,最后来解释是什么:

sql 复制代码
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

作用:使指定列的数字显示长度至少达到指定的长度!超出则不管。

一些细节部分:

  1. MySQL 8.0+ 的优化行为:
    如果不需要 ZEROFILL,可以忽略显示宽度,它不影响实际数据存储。
  2. zerofill字段只能给整数使用!

该字段就是对数据进行格式化控制的!只有显示的时候是这么长,筛选,使用的时候不是:

sql 复制代码
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即可!

sql 复制代码
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标志!

查询表的创建:

sql 复制代码
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(字段列表)


前面说:一张表只能有一个主键,但是不代表逐渐内只有一列!

如果说,我们今天并不是想以某个单一的字段作为主键,也是可以声明复合键的:

sql 复制代码
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也是,二者存在冲突!)

sql 复制代码
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使用的!)

sql 复制代码
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

建立一个带有自增长字段的表:

sql 复制代码
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(简写)!

sql 复制代码
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 主表(主表的列)

sql 复制代码
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)

现在我们先向班级表中分别添加一些数据:

sql 复制代码
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中如何使用外键来对两个表进行约束!

相关推荐
shaohaoyongchuang3 小时前
9-mysql编程
数据库
m0”-“0m3 小时前
MySQL、Nignx和Docker在Linux上的安装详解
linux·数据库·mysql
野犬寒鸦3 小时前
从零起步学习Redis || 第十章:主从复制的实现流程与常见问题处理方案深层解析
java·服务器·数据库·redis·后端·缓存
极限实验室4 小时前
Elasticsearch 备份:snapshot 镜像使用篇
数据库·elasticsearch
武子康4 小时前
Java-145 深入浅出 MongoDB 基本操作详解:数据库查看、切换、创建集合与删除完整教程
java·数据库·sql·mysql·mongodb·性能优化·系统架构
阿巴~阿巴~5 小时前
Centos 7/8 安装 Redis
linux·服务器·数据库·redis·centos
刘大猫.5 小时前
mysql数据库压缩
数据库·mysql·压缩·mysql数据库压缩·数据库压缩·数据库备份与压缩
oracle04065 小时前
sql练习题单-知识点总结
数据库·sql
会飞的架狗师5 小时前
【MySQL体系】第4篇:MySQL 查询优化实用技巧
数据库·mysql