MySQL 第五讲---基础篇 表的约束
前言:
在上一讲中,我们深入学习了 表的数据类型,掌握了如何通过 INT
、VARCHAR
、DATE
等类型精确描述数据的存储格式,确保每一列都能承载符合预期的内容。然而,仅仅定义数据类型还远远不够——如何保证数据的完整性与业务规则的严谨性?
试想,若一张用户表中允许重复的身份证号、缺失的联系方式,或商品价格出现负数,这样的数据不仅毫无意义,还会导致业务逻辑混乱甚至系统崩溃。此时,表的约束(Constraints) 便成为数据库设计的核心工具。通过约束,我们可以为数据添加“规则之锁”,例如:
-
主键(PRIMARY KEY) 确保唯一标识每一行;
-
唯一约束(UNIQUE) 防止重复值;
-
非空约束(NOT NULL) 强制关键字段必填;
-
外键(FOREIGN KEY) 维护表间的关联关系;
-
默认值(DEFAULT) 为字段提供“保底”数据;
本节课程将从零开始,系统讲解如何通过约束为数据表构建坚实的“规则屏障”,让数据库真正成为可靠的数据管家。理解约束,是迈向规范化数据库设计的关键一步!
目录
前言:
一、表的约束
二、空约束
空属性
三、默认约束
默认值
not null 和 default 结合
四、列描述
五、zerofill约束
六、主键
删除主键
追加主键
复合主键
设计主键的原则:
七、自增长
八、索引
九、唯一键
十、外键
十一、综合案例
总结:
一、表的约束
真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据的正确性。比如有一个字段是email,要求是唯一的。
在MySQL中,约束(Constraints) 是指在创建表时规定的对表中的数据进行限制的条件。
它可以对表中某列或某几列添加一些限制条件,以保证表中的数据符合要求,MySQL中的约束包括以下几种:
- 主键约束(Primary Key Constraint):用于标识表中每条记录的唯一性,每张表只能有一个主键,主键值不能重复且不能为空。
- 唯一约束(Unique Constraint):用于保证某个列的数据唯一性,每个表可以有多个唯一约束。
- 非空约束(Not Null Constraint):用于保证某个列的值不能为空,一个表中可以有多个非空约束。
- 外键约束(Foreign Key Constraint):用于关联两个表的数据,确保两个表之间的关联关系是有效的。外键约束必须在关联表的列上定义,他会限制在被关联的表中不能存在没有对应主键或唯一键值的记录。
二、空约束
空属性
MySQL中的 空属性 指的是一个字段没有值的情况,可以使用null关键字来表示。
null是一个特殊的值,表示一个未知的、不存在的或不适用的值。
与之相对的是空字符串(' '), 它表示一个空的字符串值,不同于null。
在MySQL中,可以在列定义时指定该列是否允许为空。
如果列允许为空,则该列可以包含null值。否则,该列必须包含非null值。
空属性在数据库中很常见,比如一个订单表中,如果订单尚未付款,则该订单的付款时间段可能为空。在查询时,可以使用is null 或 is not null 运算符来判断某个字段是否为空。
数据库默认字段基本都是字段为空,但是实际开发时,尽可能保证字段不为空,因为数据库为空没办法参与运算。(关于这一点的影响我们会在后面直观的感受到)
我们接下来演示一下创建表的时候,带上空约束。
比如通过select可以看到null的值为null。
由于空值无法参与运算,因此null值加一后得到的还是null,而不是我们可能想的1了。如下
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)
注意:关于这里为啥可以使用select计算表达式,我们会在后面的关于表的增删查改介绍。
如果要让某个字段不允许为空,在创建表的时候就可以给对应字段设置not null属性。
比如我们创建一个班级表,表当中包含班级名和该班级所在的教室,如果插入数据时不想让后一个字段为空,就可以在创建表时给后面的字段设置not null属性。如下:
mysql> show create table class\G;
*************************** 1. row ***************************Table: class
Create Table: CREATE TABLE `class` (`class_name` varchar(10) DEFAULT NULL,`num` int NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
1 row in set (0.00 sec)
向表中插入记录时只有后面的字段不为空时才能插入成功,否则将会插入失败。如下:
mysql> insert into class value ("高三一班");
//后面的班级编号为空,直接报错
ERROR 1136 (21S01): Column count doesn't match value count at row 1
mysql> insert into class value ("高三一班",123);
Query OK, 1 row affected (0.01 sec)mysql> select * from class;
+--------------+-----+
| class_name | num |
+--------------+-----+
| 高三一班 | 123 |
+--------------+-----+
1 row in set (0.00 sec)
三、默认约束
默认值
默认值
- 如果某一个字段会经常性的出现某个值,那么就可以考虑将这个值设置成该字段的默认值。
- 向表中插入数据时如果不给带有默认值的字段赋值,那么就会使用默认值进行插入。
默认值可以是一个常量值,也可以是一个函数表达式,比如 CURRENT_TIMESTAMP 表示当前地时间戳。当使用函数表达式时,每次插入数据时该表达式都会被重新计算并填充为该列地默认值。
在创建表时可以通过 default
关键字来指定列地默认值。
这里我们在创建表时,指定了姓名不能为空,年龄默认为18岁,性别为默认值。因此我们在插入’李四’时,未指定性别、默认为男;在插入’王五’时,未指定年龄,默认为18岁;在插入‘赵六’时,啥都没加。结果如下:
mysql> create table person( name varchar(10) not null, gender enum('男','女') default '男', age int default 18 );
Query OK, 0 rows affected (0.04 sec)mysql> desc person;
+--------+-------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------------+------+-----+---------+-------+
| name | varchar(10) | NO | | NULL | |
| gender | enum('男','女') | YES | | 男 | |
| age | int | YES | | 18 | |
+--------+-------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)mysql> insert into person values ('张三','女','19');
Query OK, 1 row affected (0.00 sec)mysql> insert into person (name,age) values ('李四',19);
Query OK, 1 row affected (0.01 sec)mysql> insert into person (name,gender) values ('王五','男');
Query OK, 1 row affected (0.01 sec)mysql> insert into person (name) values ('赵六');
Query OK, 1 row affected (0.00 sec)mysql> select * from person;
+--------+--------+------+
| name | gender | age |
+--------+--------+------+
| 张三 | 女 | 19 |
| 李四 | 男 | 19 |
| 王五 | 男 | 18 |
| 赵六 | 男 | 18 |
+--------+--------+------+
4 rows in set (0.00 sec)
结果就是我们没有填的值都被写成了默认值
not null 和 default 结合
- default 的意义是不显示地向指定列插入数据,default会起效果插入默认值。
- not null 的意义是如果显示地向指定列插入null,not null 进行约束不允许插入 null。如果只有default约束,没有not null约束,则可以向该列插入null。
- not null 和 default 一般不需要同时出现,因为default本身有默认值,不会为空。
四、列描述
列描述(comment) 是一个可选的属性,可以用来描述表中每个列的含义、作用、限制等信息。
它通常在创建表时定义,并可以通过 show create table 语句查看。
列描述不会影响数据库的结构和功能,它只是一个用于更好的理解和维护表结构的工具。
列描述就是一个类似于注释的东西。
列描述通常用于以下目的:
- 说明列的含义和作用,方便开发人员和维护人员理解表的结构。
- 限制或规定数据的输入范围、格式、长度等,防止数据异常或错误的输入。
- 记录表或列的修改历史,方便维护人员进行版本管理和和回溯。
比如创建一个用户表,表当中包含用户名、用户的年龄和用户的性别,在gender字段后面添加上对应的列描述。如下:
mysql> create table user(-> id int not null,-> age int,-> gender enum('男','女') comment "性别"-> );
Query OK, 0 rows affected (0.03 sec)mysql> show create table user\G;
*************************** 1. row ***************************Table: user
Create Table: CREATE TABLE `user` (`id` int NOT NULL,`age` int DEFAULT NULL,`gender` enum('男','女') DEFAULT NULL COMMENT '性别'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
创建表完毕后,通过show create table 表名
SQL可以看到创建表时的相关细节,包括列描述。如上:
列描述说明了每个列的含义和作用,以及表的含义。这可以帮助开发人员更好地理解表的结构,规范数据的输入,并且方便维护人员进行版本管理和回溯。
五、zerofill约束
通常情况下,MySQL会省略数值类型数据类型中的前导零。
zerofill 是一种列属性,用于将数字类型的列填充为固定长度,填充的内容改为0。
在使用zerofill属性时,如果插入的数据长度小于指定长度。
这个属性通常用于需要固定长度列,比如身份证号码等。
需要注意的是,zerofill只对数值类型的数据类型有效(即:int,tinyint等),对于其他类型的列属性,无法设置zerofill约束。
对于字节数固定的数值类型,后面的圆括号内的数字 如:int(len) 表示字段的显示宽度,即在输出时该字段最多显示的字符数,它只是为了控制该字段在输出时的显示效果,而不会影响该字段在存储时的大小和范围。如果超过了定义的显示宽度,则将该数字进行原样显示。
比如创建一个表,表当中包含a和b两列整型数据,将它们的显示宽度都设置成5,但是只有b设置zerofill属性。如下:
mysql> create table A(-> a int(5),-> b int(5) zerofill-> );
Query OK, 0 rows affected, 3 warnings (0.03 sec)mysql> desc A;
+-------+--------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------------------+------+-----+---------+-------+
| a | int | YES | | NULL | |
| b | int(5) unsigned zerofill | YES | | NULL | |
+-------+--------------------------+------+-----+---------+-------+
2 rows in set (0.01 sec)mysql> insert into A values(1,1);
Query OK, 1 row affected (0.01 sec)mysql> insert into A values(2,2);
Query OK, 1 row affected (0.01 sec)mysql> select * from A;
+------+-------+
| a | b |
+------+-------+
| 1 | 00001 |
| 2 | 00002 |
+------+-------+
2 rows in set (0.00 sec)
由于b列数据的显示宽度为5,因此查看表中数据可以看到b列数据中宽度不足5位的数据都自动在前面填充0了。
需要注意的是,zerofill属性的作用就是让数据以特定的方式进行显示而已,数据底层的储存方式并没有发生变化,通过hex函数可以看到b列中显示的00001在底层实际储存的还是1。如下:
mysql> select b,hex(b) from A;
+-------+--------+
| b | hex(b) |
+-------+--------+
| 00001 | 1 |
| 00002 | 2 |
+-------+--------+
使用zerofill 属性的主要优点是、
- 可以确保数据以相同的格式存储,这在某些情况下可以提高查询和排序的性能。
- 此外,它还可以是数据更易于阅读和比较。
注意:hex是一个函数,它的作用是将输入的数据转换为其十六进制字符串表示形式。
六、主键
主键(Primary Key)
:用于唯一标识表中每一条记录,确保记录的唯一性和完整性。
特点:主键列不允许重复,不允许为空。一个表中最多只能有一个主键,主键的所在列通常是整数类型。
比如创建一个学生表,表当中包含学生的学号和姓名,由于学生的学号是不会重复的,因此可以将其设置成主键。如下:
mysql> create table student( id int primary key not null comment "学生的学号", class int not null );
Query OK, 0 rows affected (0.02 sec)mysql> show create table student\G;
*************************** 1. row ***************************Table: student
Create Table: CREATE TABLE `student` (`id` int NOT NULL COMMENT '学生的学号',`class` int NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
创建表成功后查看表结构,可以看到id对应的Key列出现了PRI,这表示我们已经成功将学号设置成这张表的主键了。如下:
mysql> desc student;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| class | int | NO | | NULL | |
+-------+------+------+-----+---------+-------+
2 rows in set (0.00 sec)
所谓的主键约束就是,插入表中的记录的主键字段不能重复,如果插入记录的主键与表中已有记录的主键重复,这时就会因为主键冲突而插入失败;而且插入的数据不能为空,为空同样会失败。如下:
mysql> insert into student values(1,101);
Query OK, 1 row affected (0.00 sec)mysql> insert into student values(2,101);
Query OK, 1 row affected (0.00 sec)mysql> insert into student (class) values(101);
ERROR 1364 (HY000): Field 'id' doesn't have a default value
mysql> insert into student values(1,101);
ERROR 1062 (23000): Duplicate entry '1' for key 'student.PRIMARY'
mysql> select * from student;
+----+-------+
| id | class |
+----+-------+
| 1 | 101 |
| 2 | 101 |
+----+-------+
2 rows in set (0.00 sec)
删除主键
使用alter table 表名 drop primary key
即可删除指定表的主键,因为一个表只有一个主键,因此删除主键时只用指明要删除哪张表的主键即可。比如这里删除学生表的主键后再查看表结构,可以看到id对应的Key列的PRI已经没有了。如下:
mysql> alter table student drop primary key;
Query OK, 2 rows affected (0.07 sec)
Records: 2 Duplicates: 0 Warnings: 0mysql> desc student;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| id | int | NO | | NULL | |
| class | int | NO | | NULL | |
+-------+------+------+-----+---------+-------+
2 rows in set (0.00 sec)
追加主键
当表创建好之后但没有主键的时候,可以再次追加主键。
但是需要注意的是,只有列当中的值不为空并且列内的值不重复的列才能被设置成主键。比如这里重新将学号设置成学生表的主键后再查看表结构,可以看到id对应的Key列的PRI又回来了。如下:
mysql> alter table student add primary key(id);
Query OK, 0 rows affected (0.06 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> desc student;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| class | int | NO | | NULL | |
+-------+------+------+-----+---------+-------+
2 rows in set (0.00 sec)
复合主键
- 复合主键用来唯一约束多个字段里面的数据,表当中每条记录的这多个字段不能同时重复也不能为空。
- 复合主键是指在一个表中,主键由多个列组成而不是单独的一列。这样的设计可以更准确地表示实际数据地唯一性,因为在复杂地应用场景中,单独的一列很可能不能完全确定唯一性。
注意:复合主键不同于单一地主键,其顺序是有意义的。因此需要根据实际情况来确定复合主键的顺序,以确保能够把正确地反映数据的唯一性。
比如创建一个进程表,表当中包含进程的IP地址、端口号和进程的相关信息,并将IP地址和端口号组合起来形成一个复合主键。如下:
mysql> create table process(-> ip int not null,-> port int not null,-> primary key(ip,port)-> );
Query OK, 0 rows affected (0.04 sec)mysql> show create table process\G;
*************************** 1. row ***************************Table: process
Create Table: CREATE TABLE `process` (`ip` int NOT NULL,`port` int NOT NULL,PRIMARY KEY (`ip`,`port`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.01 sec)ERROR:
No query specified
表创建完毕后查看表结构,可以看到ip和port的Key列都有PRI标志,并且它们都是不允许为空的。如下:
mysql> desc process;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| ip | int | NO | PRI | NULL | |
| port | int | NO | PRI | NULL | |
+-------+------+------+-----+---------+-------+
2 rows in set (0.00 sec)
在向进程表中插入数据时,只有插入进程的IP和端口均重复时才会产生主键冲突,否则就允许插入。如下:
mysql> insert into process values (100,10);
Query OK, 1 row affected (0.00 sec)mysql> insert into process values (100,11);
Query OK, 1 row affected (0.01 sec)mysql> insert into process values (110,10);
Query OK, 1 row affected (0.00 sec)mysql> insert into process values (100,10);
ERROR 1062 (23000): Duplicate entry '100-10' for key 'process.PRIMARY'
mysql> select * from process;
+-----+------+
| ip | port |
+-----+------+
| 100 | 10 |
| 100 | 11 |
| 110 | 10 |
+-----+------+
3 rows in set (0.00 sec)
当然关于复合主键的删除与追加,大家看上面推断就好了。
设计主键的原则:
- 唯一性:主键必须保证表中每一行数据都有唯一的标识符,避免数据冲突。
- 稳定性:主键应该是一个稳定的标识符,不随数据的变化而变化。
- 简洁性:主键应该尽可能的简洁,使其在索引、关联、聚合等操作中具有高效性。
- 可读性:主键可以是自然键(如身份证号码、学号等)或人工键(如自增长 ID),需要根据实际业务情况进行选择。注:可以将选择与业务无关的值作为主键,这样的好处是业务调整不会影响主键的整体表结构。
- 设计主键时需要根据具体的业务需求来确定,一般情况下可以选择使用自增长 ID 作为主键,也可以选择一个稳定、唯一、简洁的自然键作为主键。
七、自增长
自增长
自增长(Auto Increment) 是MySQL中一种常见的特殊类型的列属性,用于再插入数据时自动分配递增的值。一般来说,自增长属性适用于那些需要每次插入一条新纪录时,自动生成一个唯一的、递增的编号的表中的列。自增长通常和主键搭配使用,作为逻辑主键。
自增长的特点:
- 任何一个字段要做自增长,前提是本身是一个索引(key一栏有值)自增长字段必须是整数
- 设置了自增长属性的字段,插入数据时如果不给该字段值,那么系统会自动找出当前字段当中已有的最大值,将最大值进行加一后的值插入该字段。
- 一张表最多只能有一个自增长。
比如创建一个表,表当中包含id和name,将id同时设置成主键和自增长字段。如下:
mysql> create table B( id int not null primary key auto_increment, name varchar(10) not null );
Query OK, 0 rows affected (0.03 sec)mysql> desc B;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(10) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
向表中插入第一条记录时如果没有指明自增长字段的值,那么自增长字段的值默认将会从1开始。 后续向表中插入记录时如果也不指明自增长字段的值,那么自增长字段的值就会依次递增。
向表中插入记录时如果不指明自增长字段的值,那么自增长字段的值将会从id列中找出最大值,将最大值加一后得到的值作为自增长字段的值进行插入。
如下:
mysql> insert into B (name) values('张三');
Query OK, 1 row affected (0.01 sec)mysql> insert into B (name) values('李四');
Query OK, 1 row affected (0.00 sec)mysql> insert into B (id,name) values(10,'李四');
Query OK, 1 row affected (0.00 sec)mysql> insert into B (name) values('王五');
Query OK, 1 row affected (0.00 sec)mysql> select * from B;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 李四 |
| 10 | 李四 |
| 11 | 王五 |
+----+--------+
4 rows in set (0.00 sec)
当然,插入记录的时候也可以指明自增长字段的值,此时将会使用该值进行插入,但注意指明的值不能和表中已有的id值重复。如下:
mysql> select * from B;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 李四 |
| 10 | 李四 |
| 11 | 王五 |
+----+--------+
4 rows in set (0.00 sec)mysql> insert into B (id,name) values(10,'李四1');
ERROR 1062 (23000): Duplicate entry '10' for key 'B.PRIMARY'
说明一下: 一般自增长字段设置后就不需要手动为该字段插入值了,直接让其从1开始进行自增长即可。
八、索引
这部分是很重要的,后面会有专门的博客介绍,本部分现在做了解。
在关系型数据库中,索引 是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单,可以提高对数据库表中数据的访问速度。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。
索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可以快速访问数据库表中得特定信息。
- 主键索引:主键索引是一种唯一性索引,用于确保表中每一行数据的唯一性。主键索引可以自动创建,也可以手动指定,通常情况下使用自增长整数作为主键。
- 唯一索引:唯一索引也是一种唯一性索引,与主键索引类似,但允许为空。唯一索引可以用于确保表中某一列的唯一性。
- 普通索引:普通索引是最常用的一种索引类型,用于加速对表中数据的查找速度。普通索引可以用于单列或多列,可以使用B树或hash算法实现。
- 全文索引:全文索引是一种特殊类型的索引,用于加速对表中文本类型数据的查找速度。全文索引支持自然语言搜索和布尔搜索等多种搜索方式。
虽然索引可以提高查询性能,但是在设计索引时也需要注意,过多或不必要的索引会影响数据更新的性能,增加数据存储空间,并且索引的设计需要结合具体业务场景和查询需求。
九、唯一键
唯一键(Unique Key) 是一种约束,
它用于保证某个列的数据唯一性,每个表可以有多个唯一约束。
与主键不同的是,唯一键允许空值,即可以在列中包含空值,但不能有重复值。
在现实生活中,我们身上有非常多具有唯一性的值,如身份证号、QQ号等。
一般而言,主键只是众多具有唯一性的属性列中的一列,该列被选择成为主键。
但这并不意味着其他具有唯一性的列不需要保证唯一性。
但是主键只能有一个,所以MySQL就提供了另一个保证列信息唯一性的方法:唯一键。唯一键和主键并不冲突,两者时互相补充的,共同维护表的完整性!
比如创建一个学生表,表当中包含学生的id、姓名和电话号码,将我们选择id作为主键,但同时每个学生的电话号码也应该具有唯一性约束,因此应该将电话号码设置成唯一键。
如下:
mysql> create table student( id int not null primary key auto_increment, name varchar(10) not null, telphone int unsigned unique );
Query OK, 0 rows affected (0.03 sec)mysql> desc student;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(10) | NO | | NULL | |
| telphone | int unsigned | YES | UNI | NULL | |
+----------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec
表创建完毕后查看表结构,可以看到tel的Key列出现了UNI标志,这就表明tel已经成功被设置成唯一键了。
向表中插入记录时,如果插入记录中的电话号码与表中已有记录的电话号码出现重复,那么就会因为唯一键冲突而插入失败。如下:
mysql> insert into student values (1,'张三',123);
Query OK, 1 row affected (0.00 sec)mysql> insert into student (name,telphone) values ('张三',124);
Query OK, 1 row affected (0.00 sec)mysql> insert into student (name,telphone) values ('张三',123);
ERROR 1062 (23000): Duplicate entry '123' for key 'student.telphone'
mysql> select * from student;
+----+--------+----------+
| id | name | telphone |
+----+--------+----------+
| 1 | 张三 | 123 |
| 2 | 张三 | 124 |
+----+--------+----------+
2 rows in set (0.00 sec)
此外,向表中插入的记录可以不指明唯一键字段的值,此时该字段默认为空,不做唯一性比较。如下:
mysql> insert into student (name) values ('张三');
Query OK, 1 row affected (0.00 sec)mysql> select * from student;
+----+--------+----------+
| id | name | telphone |
+----+--------+----------+
| 1 | 张三 | 123 |
| 2 | 张三 | 124 |
| 4 | 张三 | NULL |
+----+--------+----------+
3 rows in set (0.00 sec)
十、外键
外键
用来定义主表和从表之间的关系,外键约束主要定义在从表上,主表必须有主键约束或唯一键约束。- 外键定义后,要求插入外键列的数据必须在主表对应的列存在或为null。
比如先创建一个班级表作为主表,表当中包含班级的id和班级名,并将班级id设置为主键。
再创建一个学生表作为从表,表当中包含学生的id、姓名以及学生所在班级对应的id,并将学生表中的班级id列设置成外键,关联到班级表中的班级id列。
表创建完毕后查看学生表的表结构,可以看到学生表中的班级id对应的Key列出现了MUL标志,这表明class_id已经成功被设置成了外键。如下:
如下:
mysql> create table class(-> id int primary key not null auto_increment,-> name varchar(10)-> );
Query OK, 0 rows affected (0.03 sec)mysql> create table student(-> id int primary key auto_increment,-> name varchar(10),-> class_id int -> );
Query OK, 0 rows affected (0.03 sec)mysql> alter table student add foreign key(class_id) references class(id);
Query OK, 0 rows affected (0.07 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> desc student;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(10) | YES | | NULL | |
| class_id | int | YES | MUL | NULL | |
+----------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
为了演示外键约束,我们先向班级表中插入两条记录。如下:
mysql> insert into class values (1,'111');
Query OK, 1 row affected (0.01 sec)mysql> insert into class values (2,'112');
Query OK, 1 row affected (0.01 sec)mysql> select * from class;
+----+------+
| id | name |
+----+------+
| 1 | 111 |
| 2 | 112 |
+----+------+
2 rows in set (0.00 sec)
这时向学生表中插入记录时,如果插入的记录对应的班级id是班级表中存在的,或者插入的班级id为null,那么此时是允许进行插入的。
但如果插入学生表的记录对应的班级id是3,相当于插入学生表的这条记录对应的班级并不存在,此时将会插入失败,这就是外键约束。如下:
mysql> insert into student values (1,'张三',1);
Query OK, 1 row affected (0.01 sec)mysql> insert into student values (2,'李四',2);
Query OK, 1 row affected (0.01 sec)mysql> insert into student values (3,'赵武',3);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))
mysql> select * from student;
+----+--------+----------+
| id | name | class_id |
+----+--------+----------+
| 1 | 张三 | 1 |
| 2 | 李四 | 2 |
+----+--------+----------+
2 rows in set (0.00 sec)
这时如果向班级表中插入班级id为3的班级信息,然后再向学生表中插入上述记录,这时就允许插入了。如下:
mysql> insert into class values (3,'2222');
Query OK, 1 row affected (0.01 sec)mysql> insert into student values (3,'赵武',3);
Query OK, 1 row affected (0.00 sec)mysql> select * from student;
+----+--------+----------+
| id | name | class_id |
+----+--------+----------+
| 1 | 张三 | 1 |
| 2 | 李四 | 2 |
| 3 | 赵武 | 3 |
+----+--------+----------+
3 rows in set (0.00 sec)
说明一下:
- 理论上来说,我们创建班级表和学生表后就算不设置外键,在语义上其实也已经有了外键,但这样我们没办法保证后续插入学生表的记录中的班级id的正确性。
- 而我们给学生表中的班级id设置成外键后,外键约束就能保证只有班级id在班级表中存在的记录才能插入学生表,否则就会插入失败。
- 实际建立外键的本质就是把相关性交给MySQL去审核了,提前告诉MySQL表之间的约束关系,当用户插入不符合业务逻辑的数据时,MySQL就不允许你进行插入。
十一、综合案例
有一个商店的数据,记录客户及购物情况,由以下三个表组成:
- 商品表goods:商品编号goods_id,商品名称goods_name,单价unitprice,商品类别category,供应商provider。
- 客户表customer:客户编号customer_id,姓名name,住址address,邮箱email,性别sex,身份证card_id。
- 购买表purchase:订单号order_id,客户编号customer_id,商品编号goods_id,购买数量nums。
要求:
- 设置主键、外键。
- 客户的姓名不能为空值。
- 邮箱不能重复。
- 客户的性别(男,女)。
大家可不可以根据上面的知识设计出来一批合适符合要求的表呢?
关于讲解我放在下一讲当中。
总结:
通过本讲的学习,我们掌握了如何通过 主键、唯一约束、非空约束、外键 等工具为数据表设置规则,确保数据的准确性、完整性和一致性。约束是数据库设计的“守门人”,它从底层阻止无效数据的侵入,为业务逻辑的稳健运行保驾护航。
然而,设计合理的约束仅仅是数据管理的第一步。当数据规则确立后,如何高效地操作这些数据? 下一讲我们将进入 表的增删查改(CRUD),学习如何通过 INSERT
、DELETE
、UPDATE
和 SELECT
语句对数据进行灵活操控,并深入理解约束在数据操作中的验证机制。
从规则定义到数据操作,每一步都环环相扣。掌握约束,方能游刃有余;精通CRUD,才能释放数据的真正价值!我们下期见!