当前位置:网站首页>【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 null和default同时设置的情况下,依然可以不填值而采用默认值,不会报错。但不建议一起使用,在必须填入数据的字段中只用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 null或default那样对表的数据有强影响性,这样我们在查看时也更容易解读该字段的信息。
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就会检查出来,不允许你插入。
边栏推荐
- String analysis and use
- Abbexa low sample size chicken lysozyme C (Lyz) ELISA Kit
- 学IT毕业后该去哪个城市?哪个岗位薪资高?哪些公司待遇好?
- 01js基础 null与undefined区别 类型转换 == 代码块 逻辑运算符
- Summary of common mysql8 commands in centos7 environment
- 在模仿学习中进步的智能机器人
- Cordova Plugin /JPush PhoneGap 极光推送_本地推送_消息推送
- 旋转导航栏
- [nk] Niuke monthly competition 51 f-average question
- Only this is the most true reason why leaders promote you. The rest is nonsense!
猜你喜欢

软件测试工程师是做什么的?

"O & M youxiaodeng" self service account unlocking tool

Introduction to abbexa bacterial genome DNA Kit

Principle of gravure overprint and factors affecting overprint

标配双安全气囊,价格屠夫长安Lumin 4.89万起售

Abbkine column exkine Pro animal cell / tissue Total Protein Extraction Kit

Will your company choose to develop data center?

2022-06-09 RK817 PMU 電池溫度檢測

微积分复习1

Shaping teenagers' comprehension ability with children's programming thinking
随机推荐
自制Table錶格
The process of keyword search in Oracle tables
Understanding deep learning attention
Abbkine column exkine Pro animal cell / tissue Total Protein Extraction Kit
2022-06-09 RK817 PMU 電池溫度檢測
Quick start to VISSIM simulation
在模仿学习中进步的智能机器人
入行须知:运维需要懂编程吗?
Leetcode advanced road - plus one
Standard dual airbags, starting from 48900 for butcher Chang'an Lumin
ThinkPHP v6.0.x反序列化漏洞复现
Leetcode advanced road - 167 Sum of two numbers II - input ordered array
System reinstallation and system performance query
ROS virtual time
Before we learn about high-performance computing, let's take a look at its history
SQL Server2019安装的详细步骤实战记录(亲测可用)
[Warning] TIMESTAMP with implicit DEFAULT value is deprecated
An analysis of SQL query optimization principle (900w+ data from 17s to 300ms)
【Microsoft Azure 的1024种玩法】七十五.云端数据库迁移之快速将阿里云RDS SQL Server无缝迁移到Azure SQL Databas
Leetcode advanced path - the first unique character in a string