当前位置:网站首页>【MySQL】表的约束

【MySQL】表的约束

2022-06-10 20:46:00 yuelinghou

一. 什么是约束?

在上一篇介绍数据类型时就引入“约束”这个概念,真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,这篇文章将重点说明MySQL中的几种约束。

下面给出约束的定义:约束本质上是MySQL通过限制用户操作的方式,来达到维护本身安全的一套完整性方案,即约束的最终目的是从业务逻辑角度保证数据的正确性、安全性。

二. 为什么要有约束?

我们知道MySQL是一套数据存储解决方法,除了完成基本的数据存储功能之外,还要保证数据尽可能的安全,减少用户误操作的可能性。

就像《三体》中的面壁计划,既然已经制造出了引力波威慑系统,有就完了,为什么还要选一个面壁者(执剑人)呢?因为全球人民需要保证引力波威慑系统的安全(即不能随意使用它),故需要选出一个统一人选去控制该系统,以保持两个文明的战略平衡,这个执剑人就是约束。

三. 如何约束?

1. 空属性 — not null

我们在注册账号或填写用户信息时,有些属性是必填的、另外的是选填的:
在这里插入图片描述
对应到数据库层面,我们在定义表中的每一个字段时,它们有两种属性:null(默认的)和not null(不为空)。数据库默认字段属性都允许为空,但是实际开发时,尽可能保证字段不为空,因为数据为空没办法参与运算。

  • 创建一个Person表,字段包括人的姓名、性别,都允许为空:
    在这里插入图片描述
  • 创建一张PersonNotNull表,也包含两个字段:姓名、性别。但是不允许为空:
    在这里插入图片描述

2. 默认值 — default

通常我们在注册社交平台账号时,需要绑定你的电话号码,对于电话号码不同国家有不同的国际区号;比如中国的国际区号就是+86,如果这个社交媒体平台是中国的,则默认中国人使用的最多,所以不选择的话区号就是+86了:
在这里插入图片描述
默认值:某一种数据会经常性的出现某个具体的值,可以在一开始就指定好,在需要真实数据的时候,用户可以选择性的使用默认值。

使用方法:定义字段时在后面加上default 默认值

举例

mysql> create table if not exists Person(
    -> name varchar(20),
    -> nationality varchar(20) default '中国',
    -> age tinyint unsigned default 0
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> desc Person;// 国籍和年龄字段使用了默认值
+-------------+---------------------+------+-----+---------+-------+
| Field       | Type                | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+-------+
| name        | varchar(20)         | YES  |     | NULL    |       |
| nationality | varchar(20)         | YES  |     | 中国    |       |
| age         | tinyint(3) unsigned | YES  |     | 0       |       |
+-------------+---------------------+------+-----+---------+-------+
3 rows in set (0.01 sec)

mysql> insert into Person (name) values ('张三');
Query OK, 1 row affected (0.00 sec)

mysql> select * from Person;
+--------+-------------+------+
| name   | nationality | age  |
+--------+-------------+------+
| 张三   | 中国        |    0 |
+--------+-------------+------+
1 row in set (0.00 sec)

mysql>

问题:not null 和 default 同时设置,会发生什么情况?

下面这段代码对此问题进行了测试。我们建表时,让nationality字段即为not null也设置默认值default;发现在插入数据时,即使不填该字段也没有报错,而且发现该字段采用了默认值:

mysql> create table if not exists Person(
    -> nationality varchar(20) not null default '中国',
    -> age tinyint unsigned
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> insert into Person (age) values (18);
Query OK, 1 row affected (0.01 sec)

mysql> select * from Person;
+-------------+------+
| nationality | age  |
+-------------+------+
| 中国        |   18 |
+-------------+------+
1 row in set (0.00 sec)

mysql>

总结:在not nulldefault同时设置的情况下,依然可以不填值而采用默认值,不会报错。但不建议一起使用,在必须填入数据的字段中只用not null,在事先确定默认值的情况下只用default。

3. 列描述 — comment

列描述(comment),没有实际含义,专门用来描述字段信息,作用相当于注释。它会根据表创建语句保存,用来给程序员或DBA来进行了解。

查看方式:列描述信息在用desc关键字查看表结构时不会显示,只能通过show查看表创建语句时出现:

mysql> create table if not exists Person(
    -> name varchar(20) comment '这是用户的姓名,必填',
    -> gender char(1) default '男' comment '这是用户的性别,默认为男性',
    -> age tinyint unsigned default 18 comment '这是用户的年龄,默认为18'
    -> );
Query OK, 0 rows affected (0.02 sec)

// 1、通过desc查看不到注释信息
mysql> desc Person;
+--------+---------------------+------+-----+---------+-------+
| Field  | Type                | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| name   | varchar(20)         | YES  |     | NULL    |       |
| gender | char(1)             | YES  |     ||       |
| age    | tinyint(3) unsigned | YES  |     | 18      |       |
+--------+---------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

// 2、通过show查看表的创建语句时可以看到注释信息
mysql> show create table Person \G
*************************** 1. row ***************************
       Table: Person
Create Table: CREATE TABLE `Person` (
  `name` varchar(20) DEFAULT NULL COMMENT '这是用户的姓名,必填',
  `gender` char(1) DEFAULT '男' COMMENT '这是用户的性别,默认为男性',
  `age` tinyint(3) unsigned DEFAULT '18' COMMENT '这是用户的年龄,默认为18'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql>

使用建议:类描述最好放到字段的最后位置写,因为它不像not nulldefault那样对表的数据有强影响性,这样我们在查看时也更容易解读该字段的信息。

4. zerofill

到目前为止,我们学习到如下两种类型后面可以加圆括号:

  • 字符类型:char(len)、varchar(len)。这里圆括号的数子决定了存储字符个数的上限。
  • 数值类型:int(int)、tinyint(int)等。这里圆括号的数字决定了显示数据的位宽,只有加上zerofill关键字才有用。

举例
我们创建一张表,其中包含两个int类型的字段a和b,规定它们的位宽都是10,只有b字段添加了关键字zerofill,向该表中插入一条数据,观察插入后的显示结果:

mysql> create table if not exists Number(
    -> a int(10),
    -> b int(10) zerofill
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> desc Number;
+-------+---------------------------+------+-----+---------+-------+
| Field | Type                      | Null | Key | Default | Extra |
+-------+---------------------------+------+-----+---------+-------+
| a     | int(10)                   | YES  |     | NULL    |       |
| b     | int(10) unsigned zerofill | YES  |     | NULL    |       |
+-------+---------------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> insert into Number values (10, 20);
Query OK, 1 row affected (0.00 sec)

mysql> select * from Number;
+------+------------+
| a    | b          |
+------+------------+
|   10 | 0000000020 |
+------+------------+
1 row in set (0.00 sec)

mysql>

可以看到b的值显示位宽是10,而a的值就是正常的显示。这就是zerofill属性的作用,如果数据本身宽度小于设定的宽度(这里设置的是10),就会自动填充0已达到设定位宽。要注意的是,这只是最后显示的结果,在MySQL中实际存储的还是20。为什么是这样呢?我们可以用hex函数来证明,该函数是以16进制形式显示数值的:

mysql> select b, hex(b) from Number;
+------------+--------+
| b          | hex(b) |
+------------+--------+
| 0000000020 | 14     |
+------------+--------+
1 row in set (0.00 sec)

mysql>

可以看出数据库内部存储的还是数字20,0000000020只是设置了zerofill属性后的一种格式化输出而已。

5. 主键 — primary key

主键(primary key)是一张表中唯一标识一行数据的字段。如果把表比作红黑树的话,表的每一行数据对应红黑树的每一个节点;表的主键对应节点中的key;而表的其他字段对应节点中的value。

表的主键有如下三个特点:

  • 一张表只有一个主键字段,该字段唯一标识表中的一行数据。
  • 主键字段默认是not null属性。
  • 表的每一行数据中主键字段的内容不能重复。

举例
在这里插入图片描述

复合主建

创建一个Address表,把ip字段和port字段组合成为复合主键,用来标识公网内唯一的一个进程:

mysql> create table if not exists Address(
    -> ip varchar(20) comment '主机的公网IP',
    -> port int unsigned comment '进程在主机内的唯一标识',
    -> name varchar(20) comment '主机用户名',
    -> primary key(ip, port)
    -> );
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> desc Address;
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| ip    | varchar(20)      | NO   | PRI | NULL    |       |
| port  | int(10) unsigned | NO   | PRI | NULL    |       |
| name  | varchar(20)      | YES  |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql>

只有复合主键中的所有字段的内容完全一致时,才算主键冲突:

mysql> insert into Address values ('127.0.1', 8080, '张三');
Query OK, 1 row affected (0.01 sec)

mysql> insert into Address values ('127.0.1', 1111, '张三');
Query OK, 1 row affected (0.17 sec)

mysql> select * from Address;
+---------+------+--------+
| ip      | port | name   |
+---------+------+--------+
| 127.0.1 | 1111 | 张三   |
| 127.0.1 | 8080 | 张三   |
+---------+------+--------+
2 rows in set (0.00 sec)

mysql>

主键修改操作

1、追加主键
当表创建好以后但是没有主键的时候,可以使用如下语句再次追加主键,但要确保追加成为主键的字段的数据唯一性:

alter table 表名 add primary key(字段列表);

2、删除主键
一张表中只有一个主键,所以删除主键时不需要指明具体字段:

alter table 表名 drop primary key;

设置表主键的建议

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

6. 自增长 — auto_increment

auto_increment:设为自增长的字段必须和主键搭配使用,作为逻辑主键。该字段可以不给值,这时会自动的被系统触发,从当前表的自增长字段中已经有的最大值进行+1操作,得到一个新的不同的值。

自增长的特点

  • 自增长字段必须是整数类型。
  • 自增长字段必须是主键或复合主键中的一个字段。
  • 一张表最多只能有一个自增长字段。

使用方法:在创建表时,给字段后加上auto_increment;如果是复合主键,需要把自增长列放在复合主键的第一个位置,也就是最左边,如下面代码所示:

// 把复合主建中的a字段设为自增长
// 这样插入的话,只需要指定非自增长的字段即可
create table t(
	a int auto_increment,
	b int,
	primary key (a,b)
);

举例
创建一张学生表,把学号设为自增长,这样每次插入一行数据时只需指定学生姓名即可,他们的学号按自增长给出的值分配:
在这里插入图片描述

当前表中一行数据都没有,我们插入一个学生叫“张三”,学号使用自增长给的值,发现自增长初始时默认给值是数字1:
在这里插入图片描述

继续插入数据,观察到自增长给的值是在原来已经有的最大值基础上+1:
在这里插入图片描述

为了再次验证这个自增长的规律,我们这次插入一条自己的“学号,姓名”数据,然后再观察自增长值的变化:
在这里插入图片描述

7. 唯一键 — unique

一张表中有往往有很多字段需要唯一性,即在一张表中该字段的数据不能重复,但是一张表中只能有一个主键,那么其他需要保证唯一性的字段怎么办呢?唯一键就可以解决表中有多个字段需要唯一性约束的问题。

比如在学校,我们需要一个学生管理系统,系统中有一个学生表,学生表中有两个字段,一个身份证号码,一个是学号,我们可以选择以身份号码作为主键。这样学号就只是一个普通的字段,MySQL没有对其提供一个安全性约束,万一不小心输入了重复的学号也检测不到,直到业务出问题才改就很麻烦。所以我们设计学生学号的时候,需要一种约束:所有的学号都不能重复,我们可以在设计表的时候将学生学号设计成为唯一键。

唯一键设置方法:定义字段时加上unique关键字。

唯一键和主键的区别

  • 唯一键允许为空,而且可以多个为空,即空字段不做为唯一性的判断依据。
  • 主键的着重点在于它作为一行数据的唯一标识;而唯一键的着重点在于保证表中该字段数据的唯一性。

举例
创建一张学信息表,其中把学号设为唯一键并通过一系列插入操作验证唯一键的特性:

// 创建一张学生信息表
mysql> create table if not exists Student(
    -> name varchar(20) not null comment '学生的姓名,不允许为空',
    -> st_id int unsigned unique comment '学生的学号,设为唯一键',
    -> telephone varchar(11) primary key comment '学生的电话号码,设为主键'
    -> );
Query OK, 0 rows affected (0.03 sec)

// 观察表的结构,唯一键字段会被标识出UNI
mysql> desc Student;
+-----------+------------------+------+-----+---------+-------+
| Field     | Type             | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+-------+
| name      | varchar(20)      | NO   |     | NULL    |       |
| st_id     | int(10) unsigned | YES  | UNI | NULL    |       |
| telephone | varchar(11)      | NO   | PRI | NULL    |       |
+-----------+------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

// 测试1:唯一键字段的数据不能重复
mysql> insert into Student (name, st_id, telephone) values ('张三', 20204912, '111xxx');
Query OK, 1 row affected (0.00 sec)

mysql> insert into Student (name, st_id, telephone) values ('李四', 20204912, '222xxx');
ERROR 1062 (23000): Duplicate entry '20204912' for key 'st_id'

// 测试二:唯一键字段可以重复为空
mysql> insert into Student (name, telephone) values ('张三', '222xxx');
Query OK, 1 row affected (0.01 sec)

mysql> insert into Student (name, telephone) values ('李四', '333xxx');
Query OK, 1 row affected (0.01 sec)

mysql> select * from Student;
+--------+----------+-----------+
| name   | st_id    | telephone |
+--------+----------+-----------+
| 张三   | 20204912 | 111xxx    |
| 张三   |     NULL | 222xxx    |
| 李四   |     NULL | 333xxx    |
+--------+----------+-----------+
3 rows in set (0.00 sec)

8. 外键 — foreign key

外键用于定义主表和从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或unique约束。当定义外键后,要求从表的外键字段数据必须存在于主表的主键(or唯一键)列存在或为本身为NULL。

理解外键
在这里插入图片描述

外键设置方法
在创建表时单起一行来专门设置外键:

foreign key (从表里的字段名) references 主表(主表里的字段名)

举例
创建一张学生表,里面有学生的名字和班级号;再创建一张班级表,里面存有各个班级对应的科目信息。

用外键把学生表里的班级号和班级表里的班级号关联起来:

// 主表必须先创建出来
mysql> create table if not exists ClaTable(
    -> id int unsigned primary key comment '班级号',
    -> course varchar(20) not null comment '班级课程'
    -> );
Query OK, 0 rows affected (0.02 sec)

// 从表在创建时可以选择自己的某个字段与主表的字段进行外键约束
mysql> create table if not exists StuTable(
    -> name varchar(20) not null comment '学生姓名',
    -> class_id int unsigned comment '学生所在班级',
    -> foreign key(class_id) references ClaTable(id)
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> desc ClaTable;
+--------+------------------+------+-----+---------+-------+
| Field  | Type             | Null | Key | Default | Extra |
+--------+------------------+------+-----+---------+-------+
| id     | int(10) unsigned | NO   | PRI | NULL    |       |
| course | varchar(20)      | NO   |     | NULL    |       |
+--------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

// 观察到从表中的class_id字段键信息为MUL
mysql> desc StuTable;
+----------+------------------+------+-----+---------+-------+
| Field    | Type             | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+-------+
| name     | varchar(20)      | NO   |     | NULL    |       |
| class_id | int(10) unsigned | YES  | MUL | NULL    |       |
+----------+------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

我们先在主表ClaTable中插入两条班级相关的数据:

mysql> insert into ClaTable (id, course) values (401, '物理、化学、地理');
Query OK, 1 row affected (0.00 sec)

mysql> insert into ClaTable (id, course) values (405, '历史、政治、生物');
Query OK, 1 row affected (0.00 sec)

mysql> select * from ClaTable;
+-----+--------------------------+
| id  | course                   |
+-----+--------------------------+
| 401 | 物理、化学、地理         |
| 405 | 历史、政治、生物         |
+-----+--------------------------+
2 rows in set (0.00 sec)

接下来向从表StuTable中插入数据来验证外键的特点:

  • 插入一个班级号为500班的学生,因为主表中没有这个班级,所以插入失败,这时外键约束在起作用:
    在这里插入图片描述
  • 从表里外键字段的数据是允许为NULL的。在从表中插入班级id为null的一个学生,意为来了一个新学生,目前还没有分配班级:
    在这里插入图片描述

理解外键和外键约束

上面的例子,我们不创建外键约束,就正常建立学生表,以及班级表,该有的字段我们都有,然后通过对照学生表和班级表的id信息也能让两张表关联起来,此时,在实际使用的时候,可能会出现什么问题?

有没有可能插入的学生信息中不小心输错了班级号,但是班级表中并没有这个班级,这时MySQL并没有检查出来,时两张表是有业务上的相关性的,但是在业务上并没有建立约束关系,那么在后面业务中就可能出现问题。

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

原网站

版权声明
本文为[yuelinghou]所创,转载请带上原文链接,感谢
https://blog.csdn.net/m0_51064412/article/details/125115233