十四、MySQL 约束详解

前置知识:

一、数据库开发与实战专栏导学及数据库基础概念入门
二、MySQL 介绍及 MySQL 安装与配置
三、MySQL 数据库的基本操作
四、MySQL 存储引擎及数据类型
五、数据导入与基本的 SELECT 语句
六、MySQL 数据库练习题1(包含前5章练习题目及答案)
七、MySQL 多表查询详解(附练习题及答案----超详细)
八、MySQL 常用函数汇总(1)
九、MySQL 常用函数汇总(2)
十、MySQL 聚合函数、分组查询及过滤分组
十一、子查询详解
十二、创建和管理表
十三、表数据的增、删、改操作

数据完整性是指数据的正确性和相容性,是为了防止数据库中存在不符合语义的数据,即防止数据库中存在不正确的数据。在 MySQL 中提供了多种完整性约束,它们作为数据库关系模式定义的一部分,可以通过 CREATE TABLEALTER TABLE 语句来定义。一旦定义了完整性约束,MySQL 服务器会随时检测处于更新状态的数据库内容是否符合相关的完整性约束,从而保证数据的一致性与正确性。这样,既能有效地防止对数据库的意外破坏,又能提高完整性检测的效率,还能减轻数据库编程人员的工作负担。本文将对数据完整性约束进行详细介绍。本章知识架构及重难点如下:
请添加图片描述

一、定义完整性约束

关系模型的完整性规则是对关系的某种约束条件。在关系模型中,提供了实体完整性、参照完整性和用户定义完整性3项规则。下面将分别介绍 MySQL 中对数据库完整性3项规则的设置和实现方式。

1.1 实体完整性

实体( Entity ) 是一个数据对象,是指客观存在并可以相互区分的事物,如一个教师、一个学生或一个雇员等。一个实体在数据库中表现为表中的一条记录。通常情况下,它必须遵守实体完整性规则。

实体完整性规则(Entity Integrity Rule)是指关系的主属性,即主码(主键)的组成不能为空,也就是关系的主属性不能是空值(NULL)。关系对应于现实世界中的实体集,而现实世界中的实体是可区分的,即说明每个实例具有唯一性标识。在关系模型中,是使用主码(主键)作为唯一性标识的,若假设主码(主键)取空值,则说明这个实体不可标识,即不可区分,这个假设显然不正确,与现实世界应用环境相矛盾,因此不能存在这样的无标识实体,从而在关系模型中引入实体完整性约束。例如,学生关系(学号、姓名、性别)中,学号 为主码(主键),则 学号 这个属性不能为空值,否则就违反了实体完整性规则。在 MySQL 中,实体完整性是通过 主键约束候选键约束 来实现的。

1.1.1 主键约束

主键可以是表中的某一列,也可以是表中多个列构成的一个组合;其中,由多个列组合而成的主键也称为复合主键。在 MySQL 中,主键必须遵守以下规则:

  1. 每一个表只能定义一个主键。
  2. 唯一性原则。主键的值,也称键值,必须能够唯一标识表中的每一行记录,且不能为 NULL。也就是说,一个表中两个不同的行在主键上不能具有相同的值。
  3. 最小化规则。复合主键不能包含不必要的多余列。也就是说,当从一个复合主键中删除一列后,如果剩下的列构成的主键仍能满足唯一性原则,那么这个复合主键是不正确的。
  4. 一个列名在复合主键的列表中只能出现一次。
  5. MySQL 的主键名总是 PRIMARY,就算自己命名了主键约束名也没用。

MySQL 中,可以在 CREATE TABLE 或者 ALTER TABLE 语句中,使用 PRIMARY KEY 子句来创建主键约束,其实现方式有以下两种。

1、 作为列的完整性约束。在定义表的某个列的属性时,加上 PRIMARY KEY 关键字实现。

#在创建用户信息表tb_user时,将id字段设置为主键
mysql> CREATE TABLE IF NOT EXISTS tb_user(
    -> id INT PRIMARY KEY,
    -> username VARCHAR(30),
    -> password VARCHAR(30),
    -> createtime DATETIME);
Query OK, 0 rows affected, 1 warning (0.00 sec)
#插入数据测试
mysql> CREATE TABLE IF NOT EXISTS tb_user(
    -> id INT PRIMARY KEY,
    -> username VARCHAR(30),
    -> password VARCHAR(30),
    -> createtime DATETIME);
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> INSERT INTO tb_user(id,username,`password`) VALUES(1,'amo','amo123');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_user(id,username,`password`) VALUES(2,'jerry','jerry123');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tb_user;
+----+----------+----------+------------+
| id | username | password | createtime |
+----+----------+----------+------------+
|  1 | amo      | amo123   | NULL       |
|  2 | jerry    | jerry123 | NULL       |
+----+----------+----------+------------+
2 rows in set (0.00 sec)

mysql> INSERT INTO tb_user(id,username,`password`) VALUES(1,'paul','paul123');
ERROR 1062 (23000): Duplicate entry '1' for key 'tb_user.PRIMARY'
mysql> INSERT INTO tb_user VALUES(NULL,'paul','paul123',NULL);
ERROR 1048 (23000): Column 'id' cannot be null
mysql> INSERT INTO tb_user(id,username,`password`) VALUES(3,'amo','amo123');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tb_user;
+----+----------+----------+------------+
| id | username | password | createtime |
+----+----------+----------+------------+
|  1 | amo      | amo123   | NULL       |
|  2 | jerry    | jerry123 | NULL       |
|  3 | amo      | amo123   | NULL       |
+----+----------+----------+------------+
3 rows in set (0.00 sec)

2、 作为表的完整性约束。在定义表的所有列的属性后,加上 PRIMARY KEY(index_col_name,…) 子句实现。

mysql> CREATE TABLE tb_student(
    -> id INT,
    -> `name` VARCHAR(30),
    -> sex VARCHAR(2),
    -> classid INT,
    -> birthday DATE,
    -> PRIMARY KEY(id,classid));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO tb_student(id,`name`,sex,classid) VALUES(1,'Amo','男',2);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_student(id,`name`,sex,classid) VALUES(2,'Amo','男',2);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_student(id,`name`,sex,classid) VALUES(1,'Jerry','男',2);
ERROR 1062 (23000): Duplicate entry '1-2' for key 'tb_student.PRIMARY'
mysql> INSERT INTO tb_student(id,`name`,sex,classid) VALUES(1,'Jerry','男',NULL);
#如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复
ERROR 1048 (23000): Column 'classid' cannot be null

如果主键仅由表中的某一列所构成,那么以上两种方法均可以定义主键约束;如果主键由表中多个列所构成,那么只能用第二种方法定义主键约束。另外,定义主键约束后,MySQL 会自动为主键创建一个唯一索引,默认名为 PRIMARY

mysql> SELECT * FROM information_schema.table_constraints WHERE table_name = 'tb_student';

运行上述代码,其结果如下图所示:
在这里插入图片描述
MySQL 的主键名总是 PRIMARY,就算自己命名了主键约束名也没用。如下所示:

mysql> CREATE TABLE tb_student2(
    -> id INT,
    -> `name` VARCHAR(30),
    -> sex VARCHAR(2),
    -> classid INT,
    -> birthday DATE,
    -> CONSTRAINT pk_tb_student2_id_classid PRIMARY KEY(id,classid));
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM information_schema.table_constraints WHERE table_name = 'tb_student2';
+--------------------+-------------------+-----------------+--------------+-------------+-----------------+----------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME  | CONSTRAINT_TYPE | ENFORCED |
+--------------------+-------------------+-----------------+--------------+-------------+-----------------+----------+
| def                | mysql_study       | PRIMARY         | mysql_study  | tb_student2 | PRIMARY KEY     | YES      |
+--------------------+-------------------+-----------------+--------------+-------------+-----------------+----------+
1 row in set (0.00 sec)

当创建主键约束时,系统默认会在所在的列或列组合上建立对应的主键索引(能够根据主键查询的,就根据主键查询,效率更高)。如果删除主键约束了,主键约束对应的索引就自动删除了。如下所示:

#删除主键约束,不需要指定主键名,因为一个表只有一个主键
mysql> ALTER TABLE tb_student2 DROP PRIMARY KEY;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM information_schema.table_constraints WHERE table_name = 'tb_student2';
Empty set (0.00 sec)

#删除主键约束后,非空还存在
mysql> DESC tb_student2;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id       | int         | NO   |     | NULL    |       |
| name     | varchar(30) | YES  |     | NULL    |       |
| sex      | varchar(2)  | YES  |     | NULL    |       |
| classid  | int         | NO   |     | NULL    |       |
| birthday | date        | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

注意:不要修改主键字段的值。 因为主键是数据记录的唯一标识,如果修改了主键的值,就有可能会破坏数据的完整性。补充:建表后增加主键约束:
在这里插入图片描述

1.1.2 自增列:AUTO_INCREMENT

关键字: AUTO_INCREMENT 特点和要求:

  1. 一个表最多只能有一个自增长列
  2. 当需要产生唯一标识符或顺序值时,可设置自增长
  3. 自增长列约束的列必须是键列(主键列,唯一键列)
  4. 自增约束的列的数据类型必须是整数类型
  5. 如果自增列指定了 0NULL,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接赋值为具体值。

错误演示:

mysql> CREATE TABLE temp_employee(
    -> eid INT AUTO_INCREMENT,
    -> ename 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 temp_employee(
    -> eid INT,
    -> ename VARCHAR(20) PRIMARY KEY AUTO_INCREMENT);
ERROR 1063 (42000): Incorrect column specifier for column 'ename'

建表时指定自增约束:

CREATE  TABLE table_name(
字段名 数据类型  PRIMARY KEY AUTO_INCREMENT,
.....
);

CREATE  TABLE table_name(
...
字段名 数据类型 UNIQUE KEY AUTO_INCREMENT, #UNIQUE KEY后续会进行讲解
...
);

建表:

mysql> CREATE TABLE tb_student(
    -> id INT PRIMARY KEY AUTO_INCREMENT,
    -> `name` VARCHAR(30),
    -> sex VARCHAR(2),
    -> classid INT,
    -> birthday DATE);
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM tb_student;
Empty set (0.00 sec)

mysql> INSERT INTO tb_student VALUES(1,'Amo','男',1,'1999-10-03');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_student VALUES(NULL,'Crystal','女',1,'1996-08-03');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tb_student;
+----+---------+------+---------+------------+
| id | name    | sex  | classid | birthday   |
+----+---------+------+---------+------------+
|  1 | Amo     ||       1 | 1999-10-03 |
|  2 | Crystal ||       1 | 1996-08-03 |
+----+---------+------+---------+------------+
2 rows in set (0.00 sec)

mysql> INSERT INTO tb_student VALUES(0,'Paul','男',2,'1998-08-03');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_student VALUES(10,'Paul','男',2,'1998-08-03');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tb_student;
+----+---------+------+---------+------------+
| id | name    | sex  | classid | birthday   |
+----+---------+------+---------+------------+
|  1 | Amo     ||       1 | 1999-10-03 |
|  2 | Crystal ||       1 | 1996-08-03 |
|  3 | Paul    ||       2 | 1998-08-03 |
| 10 | Paul    ||       2 | 1998-08-03 |
+----+---------+------+---------+------------+
4 rows in set (0.00 sec)
mysql> DESC tb_student;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | int         | NO   | PRI | NULL    | auto_increment |
| name     | varchar(30) | YES  |     | NULL    |                |
| sex      | varchar(2)  | YES  |     | NULL    |                |
| classid  | int         | YES  |     | NULL    |                |
| birthday | date        | YES  |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

建表后指定自增约束:

mysql> CREATE TABLE tb_student2(
    -> id INT PRIMARY KEY,
    -> `name` VARCHAR(30),
    -> sex VARCHAR(2),
    -> classid INT,
    -> birthday DATE);
Query OK, 0 rows affected (0.01 sec)

mysql> DESC tb_student2;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id       | int         | NO   | PRI | NULL    |       |
| name     | varchar(30) | YES  |     | NULL    |       |
| sex      | varchar(2)  | YES  |     | NULL    |       |
| classid  | int         | YES  |     | NULL    |       |
| birthday | date        | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

mysql> ALTER TABLE tb_student2 MODIFY id INT AUTO_INCREMENT;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> DESC tb_student2;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | int         | NO   | PRI | NULL    | auto_increment |
| name     | varchar(30) | YES  |     | NULL    |                |
| sex      | varchar(2)  | YES  |     | NULL    |                |
| classid  | int         | YES  |     | NULL    |                |
| birthday | date        | YES  |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

删除自增约束:

mysql> DESC tb_student2;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | int         | NO   | PRI | NULL    | auto_increment |
| name     | varchar(30) | YES  |     | NULL    |                |
| sex      | varchar(2)  | YES  |     | NULL    |                |
| classid  | int         | YES  |     | NULL    |                |
| birthday | date        | YES  |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
#去掉auto_increment相当于删除
mysql> ALTER TABLE tb_student2 MODIFY id INT;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> DESC tb_student2;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id       | int         | NO   | PRI | NULL    |       |
| name     | varchar(30) | YES  |     | NULL    |       |
| sex      | varchar(2)  | YES  |     | NULL    |       |
| classid  | int         | YES  |     | NULL    |       |
| birthday | date        | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

1.1.3 候选键约束或唯一约束(UNIQUE KEY)

如果一个属性集能唯一标识元组,且又不含有多余的属性,那么这个属性集称为关系的候选键。例如,在包含学号、姓名、性别、年龄、院系、班级等列的学生信息表中,学号能够标识一名学生,因此,它可以作为候选键,而如果规定,不允许有同名的学生,那么姓名也可以作为候选键,即给姓名添加 唯一约束(UNIQUE KEY)作用: 用来限制某个字段/某列的值不能重复。
在这里插入图片描述
特点:

  1. 同一个表可以有多个唯一约束。
  2. 唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一。唯一性约束允许列值为空。
  3. 在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同。
  4. MySQL 会给唯一约束的列上默认创建一个唯一索引。

建表时添加唯一约束:

mysql> CREATE TABLE tb_user (
    ->   id INT AUTO_INCREMENT UNIQUE,
    ->   `username` VARCHAR (30) NOT NULL UNIQUE,
    ->   `password` VARCHAR (30) UNIQUE,
    ->   createtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO tb_user(`username`,`password`) VALUES ('amo', 'amo123');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_user(`username`,`password`) VALUES ('jerry', NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_user(`username`,`password`) VALUES ('pauk', NULL);
Query OK, 1 row affected (0.00 sec)
mysql> CREATE TABLE tb_user1(
    -> id INT AUTO_INCREMENT PRIMARY KEY,
    -> `username` VARCHAR(30),
    -> `password` VARCHAR(30),
    -> createtime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    -> CONSTRAINT uk_tb_user1_test UNIQUE(`username`, `password`));
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO tb_user1(`username`,`password`) VALUES('amo', 'amo123');
Query OK, 1 row affected (0.01 sec)
#表示用户名和密码组合不能重复
mysql> INSERT INTO tb_user1(`username`,`password`) VALUES('amo', 'amo123');
ERROR 1062 (23000): Duplicate entry 'amo-amo123' for key 'tb_user1.uk_tb_user1_test'
mysql> INSERT INTO tb_user1(`username`,`password`) VALUES(NULL, NULL);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tb_user1;
+----+----------+----------+---------------------+
| id | username | password | createtime          |
+----+----------+----------+---------------------+
|  1 | amo      | amo123   | 2023-02-03 21:06:17 |
|  3 | NULL     | NULL     | 2023-02-03 21:06:32 |
+----+----------+----------+---------------------+
2 rows in set (0.00 sec)

建表后指定唯一约束:

#字段列表中如果是一个字段,表示该列的值唯一。如果是两个或更多个字段,那么复合唯一,即多个字段的组合是唯一的
#方式1:
ALTER TABLE 表名称 ADD UNIQUE KEY(字段列表);
#方式2:
ALTER TABLE 表名称 MODIFY 字段名 字段类型 UNIQUE;
mysql> CREATE TABLE student (
    ->   sid INT PRIMARY KEY,
    ->   sname VARCHAR (20),
    ->   spassword VARCHAR(30),
    ->   tel CHAR (11),
    ->   cardid CHAR (18)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> ALTER TABLE student ADD UNIQUE KEY(tel);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE student ADD UNIQUE KEY(sname, spassword);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> DESC student;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| sid       | int         | NO   | PRI | NULL    |       |
| sname     | varchar(20) | YES  | MUL | NULL    |       |
| spassword | varchar(30) | YES  |     | NULL    |       |
| tel       | char(11)    | YES  | UNI | NULL    |       |
| cardid    | char(18)    | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

删除唯一约束:

  1. 添加唯一性约束的列上也会自动创建唯一索引。
  2. 删除唯一约束只能通过删除唯一索引的方式删除。
  3. 删除时需要指定唯一索引名,唯一索引名就和唯一约束名一样。
  4. 如果创建唯一约束时未指定名称,如果是单列,就默认和列名相同;如果是组合列,那么默认和 () 中排在第一个的列名相同。也可以自定义唯一性约束名。

示例如下:

mysql> #查看都有哪些约束
mysql> SELECT * FROM information_schema.table_constraints WHERE table_name = 'student';
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE | ENFORCED |
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
| def                | mysql_study       | PRIMARY         | mysql_study  | student    | PRIMARY KEY     | YES      |
| def                | mysql_study       | sname           | mysql_study  | student    | UNIQUE          | YES      |
| def                | mysql_study       | tel             | mysql_study  | student    | UNIQUE          | YES      |
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
3 rows in set (0.00 sec)

mysql> #注意:可以通过 show index from 表名称; 查看表的索引
mysql> SHOW INDEX FROM student;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| student |          0 | PRIMARY  |            1 | sid         | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| student |          0 | tel      |            1 | tel         | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
| student |          0 | sname    |            1 | sname       | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
| student |          0 | sname    |            2 | spassword   | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
4 rows in set (0.00 sec)

mysql> ALTER TABLE USER DROP INDEX sname;
ERROR 1146 (42S02): Table 'mysql_study.user' doesn't exist
mysql> ALTER TABLE student DROP INDEX sname;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM information_schema.table_constraints WHERE table_name = 'student';
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE | ENFORCED |
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
| def                | mysql_study       | PRIMARY         | mysql_study  | student    | PRIMARY KEY     | YES      |
| def                | mysql_study       | tel             | mysql_study  | student    | UNIQUE          | YES      |
+--------------------+-------------------+-----------------+--------------+------------+-----------------+----------+
2 rows in set (0.00 sec)

1.2 用户定义完整性

用户定义完整性规则(User-defined Integrity Rule)是针对某一应用环境的完整性约束条件,它反映了某一具体应用所涉及的数据应满足的要求。关系模型提供定义和检验这类完整性规则的机制,其目的是由系统来统一处理,而不再由应用程序来完成这项工作。在实际系统中,这类完整性规则一般是在建立数据表的同时进行定义,应用编程人员不需要再做考虑,如果某些约束条件没有建立在库表一级,则应用编程人员应在各模块的具体编程中通过程序进行检查和控制。MySQL 支持非空约束、CHECK约束 和触发器3种用户自定义完整性约束。其中,触发器将在后续章节进行详细介绍。这里主要介绍非空约束和 CHECK约束

1.2.1 非空约束

MySQL 中,非空约束可以通过在 CREATE TABLEALTER TABLE 语句中,某个列定义后面加上关键字 NOT NULL 来定义,用来约束 该列/该字段的取值不能为空
在这里插入图片描述
特点:

  1. 默认,所有的类型的值都可以是 NULL,包括 INTFLOAT 等数据类型
  2. 非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空
  3. 一个表可以有很多列都分别限定了非空
  4. 空字符串 '' 不等于 NULL0 也不等于 NULL

建表时添加非空约束:

mysql> CREATE TABLE tb_class1(
    -> id INT NOT NULL AUTO_INCREMENT,
    -> `name` VARCHAR(45) NOT NULL,
    -> remark VARCHAR(100),
    -> PRIMARY KEY(`id`));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO tb_class1 VALUES(1,'amo','11111');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_class1 VALUES(2,NULL,'11111');
ERROR 1048 (23000): Column 'name' cannot be null
mysql> INSERT INTO tb_class1 VALUES(2,'Jerry',NULL);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tb_class1;
+----+-------+--------+
| id | name  | remark |
+----+-------+--------+
|  1 | amo   | 11111  |
|  2 | Jerry | NULL   |
+----+-------+--------+
2 rows in set (0.00 sec)

建表后添加非空约束:

mysql> SELECT * FROM tb_class1;
+----+-------+--------+
| id | name  | remark |
+----+-------+--------+
|  1 | amo   | 11111  |
|  2 | Jerry | NULL   |
+----+-------+--------+
2 rows in set (0.00 sec)

mysql> ALTER TABLE tb_class1 MODIFY remark VARCHAR(100) NOT NULL;
ERROR 1138 (22004): Invalid use of NULL value
mysql> DELETE FROM tb_class1 WHERE remark IS NULL;
Query OK, 1 row affected (0.00 sec)

mysql> ALTER TABLE tb_class1 MODIFY remark VARCHAR(100) NOT NULL;
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> INSERT INTO tb_class1 VALUES(2,'Jerry',NULL);
ERROR 1048 (23000): Column 'remark' cannot be null
mysql> INSERT INTO tb_class1 VALUES(2,'Jerry','22222');
Query OK, 1 row affected (0.00 sec)

删除非空约束:

mysql> ALTER TABLE tb_class1 MODIFY remark VARCHAR(100) NULL;
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE tb_class1 MODIFY name VARCHAR(100);
Query OK, 2 rows affected (0.02 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> INSERT INTO tb_class1 VALUES(3,'Paul',NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_class1 VALUES(4,NULL,'44444');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tb_class1;
+----+-------+--------+
| id | name  | remark |
+----+-------+--------+
|  1 | amo   | 11111  |
|  2 | Jerry | 22222  |
|  3 | Paul  | NULL   |
|  4 | NULL  | 44444  |
+----+-------+--------+
4 rows in set (0.00 sec)

1.2.2 CHECK约束

与非空约束一样,CHECK 约束也可以通过在 CREATE TABLEALTER TABLE 语句中,根据用户的实际完整性要求来定义。它可以分别对列或表实施 CHECK 约束,其中使用的语法如下:

CHECK(expr)

其中,expr 是一个 SQL表达式,用于指定需要检查的限定条件。在更新表数据时,MySQL 会检查更新后的数据行是否满足 CHECK 约束中的限定条件。该限定条件可以是简单的表达式,也可以是复杂的表达式(如子查询 但是我测试会报错!)。下面将分别介绍如何对列和表实施 CHECK 约束。

注意: MySQL5.7 可以使用 CHECK 约束,但 CHECK 约束对数据验证没有任何作用。添加数据时,没有任何错误或警告。但是 MySQL 8.0 中可以使用 CHECK 约束了。

1、对列实施 CHECK 约束。CHECK 子句置于表的某个列的定义之后就是对列实施 CHECK 约束。下面将通过一个具体的实例来说明如何对列实施 CHECK 约束示例:

#MySQL8.0中演示
mysql> CREATE TABLE tb_student2(
    -> id INT AUTO_INCREMENT,
    -> `name` VARCHAR(30) NOT NULL,
    -> sex VARCHAR(2),
    -> age INT NOT NULL CHECK(age>6 and age<18),
    -> remark VARCHAR(100),
    -> PRIMARY KEY(id));
ERROR 1050 (42S01): Table 'tb_student2' already exists
mysql> DROP table tb_student2;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE tb_student2(
    -> id INT AUTO_INCREMENT,
    -> `name` VARCHAR(30) NOT NULL,
    -> sex VARCHAR(2),
    -> age INT NOT NULL CHECK(age>6 and age<18),
    -> remark VARCHAR(100),
    -> PRIMARY KEY(id));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO tb_student2(`name`,sex,age,remark) VALUES('amo','男',17,'111111');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_student2(`name`,sex,age,remark) VALUES('crystal','女',18,'222222');
ERROR 3819 (HY000): Check constraint 'tb_student2_chk_1' is violated.
mysql> SELECT * FROM tb_student2;
+----+------+------+-----+--------+
| id | name | sex  | age | remark |
+----+------+------+-----+--------+
|  1 | amo  ||  17 | 111111 |
+----+------+------+-----+--------+
1 row in set (0.00 sec)
#MySQL5.7中演示
mysql> CREATE TABLE tb_student2(
    -> id INT AUTO_INCREMENT,
    -> `name` VARCHAR(30) NOT NULL,
    -> sex VARCHAR(2),
    -> age INT NOT NULL CHECK(age>6 and age<18),
    -> remark VARCHAR(100),
    -> PRIMARY KEY(id));
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT INTO tb_student2(`name`,sex,age,remark) VALUES('amo','男',17,'111111');
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO tb_student2(`name`,sex,age,remark) VALUES('crystal','女',18,'222222');
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> SELECT * FROM tb_student2;
+----+---------+------+-----+--------+
| id | name    | sex  | age | remark |
+----+---------+------+-----+--------+
|  1 | amo     ||  17 | 111111 |
|  2 | crystal ||  18 | 222222 |
+----+---------+------+-----+--------+
2 rows in set (0.00 sec)

2、表实施 CHECK 约束。CHECK 子句置于表中所有列的定义以及主键约束和外键定义之后就是对表实施 CHECK 约束。下面将通过一个具体的实例来说明如何对表实施 CHECK 约束。

#后续再来验证

3、修改表时设置检查约束,示例如下:

#ALTER TABLE tb_emp7 ADD CONSTRAINT <检查约束名> CHECK(<检查约束>)
mysql> CREATE TABLE tb_student3 (
    ->  id INT AUTO_INCREMENT,
    ->  `name` VARCHAR(30) NOT NULL,
    ->  sex VARCHAR(2),
    ->  classid INT NOT NULL,
    ->  birthday DATE,
    ->  remark VARCHAR(100),
    ->  PRIMARY KEY(id));
Query OK, 0 rows affected (0.01 sec)

mysql> ALTER TABLE tb_student3 ADD CONSTRAINT ck_tb_student3_classid CHECK(classid<=100 AND classid>=9);
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> INSERT INTO tb_student3(`name`,sex,classid) VALUES('crystal','女',18);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_student3(`name`,sex,classid) VALUES('crystal','女',6);
ERROR 3819 (HY000): Check constraint 'ck_tb_student3_classid' is violated.

4、删除检查约束,示例如下:

#语法:ALTER TABLE <数据表名> DROP CONSTRAINT <检查约束名>;
mysql> SELECT * FROM information_schema.table_constraints WHERE table_name = 'tb_student3';
+--------------------+-------------------+------------------------+--------------+-------------+-----------------+----------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME        | TABLE_SCHEMA | TABLE_NAME  | CONSTRAINT_TYPE | ENFORCED |
+--------------------+-------------------+------------------------+--------------+-------------+-----------------+----------+
| def                | mysql_study       | PRIMARY                | mysql_study  | tb_student3 | PRIMARY KEY     | YES      |
| def                | mysql_study       | ck_tb_student3_classid | mysql_study  | tb_student3 | CHECK           | YES      |
+--------------------+-------------------+------------------------+--------------+-------------+-----------------+----------+
2 rows in set (0.00 sec)

mysql> ALTER TABLE tb_student3 DROP CONSTRAINT ck_tb_student3_classid;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM information_schema.table_constraints WHERE table_name = 'tb_student3';
+--------------------+-------------------+-----------------+--------------+-------------+-----------------+----------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME  | CONSTRAINT_TYPE | ENFORCED |
+--------------------+-------------------+-----------------+--------------+-------------+-----------------+----------+
| def                | mysql_study       | PRIMARY         | mysql_study  | tb_student3 | PRIMARY KEY     | YES      |
+--------------------+-------------------+-----------------+--------------+-------------+-----------------+----------+
1 row in set (0.00 sec)

1.2.3 DEFAULT约束

作用: 给某个字段/某列指定默认值,一旦设置默认值,在插入数据时,如果此字段没有显式赋值,则赋值为默认值。例如,员工信息表中,部门位置在北京的较多,那么部门位置就可以默认为 北京,系统就会自动为这个字段赋值为 北京。创建表时可以使用 DEFAULT 关键字设置默认值约束,具体的语法格式如下:

#说明:1.默认值约束一般不在唯一键和主键列上加
#2.其中,默认值 为该字段设置的默认值,如果是字符串类型的,要用单引号括起来。
<字段名> <数据类型> DEFAULT <默认值>;

创建数据表 tb_dept3,指定部门位置默认为 BeijingSQL 语句和运行结果如下所示:

mysql> CREATE TABLE tb_dept3(
    -> id INT PRIMARY KEY,
    -> `name` VARCHAR(30),
    -> location VARCHAR(50) DEFAULT 'Beijing'
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO tb_dept3 VALUES (1, 'Amo');
ERROR 1136 (21S01): Column count doesn't match value count at row 1
mysql> INSERT INTO tb_dept3 VALUES (1, 'Amo', 'ChongQing');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO tb_dept3(id, `name`) VALUES (2, 'Jerry');
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM tb_dept3;
+----+-------+-----------+
| id | name  | location  |
+----+-------+-----------+
|  1 | Amo   | ChongQing |
|  2 | Jerry | Beijing   |
+----+-------+-----------+
2 rows in set (0.00 sec)

建表后添加或修改默认值约束:

ALTER TABLE <数据表名> CHANGE COLUMN <字段名> <数据类型> DEFAULT <默认值>;
#如果这个字段原来有非空约束,你还保留非空约束,那么在加默认值约束时,还得保留非空约束,否则非空约束就被删除了
#同理,在给某个字段加非空约束也一样,如果这个字段原来有默认值约束,你想保留,也要在modify语句中保留默认值约束,否则就删除了
ALTER TABLE table_name MODIFY 字段名 数据类型 DEFAULT 默认值;

修改数据表 tb_dept3,将部门位置的默认值修改为 ShanghaiSQL 语句和运行结果如下所示:

mysql> ALTER TABLE tb_dept3 CHANGE COLUMN location location VARCHAR(50) DEFAULT 'Shanghai';
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> DESC tb_dept3;
+----------+-------------+------+-----+----------+-------+
| Field    | Type        | Null | Key | Default  | Extra |
+----------+-------------+------+-----+----------+-------+
| id       | int         | NO   | PRI | NULL     |       |
| name     | varchar(30) | YES  |     | NULL     |       |
| location | varchar(50) | YES  |     | Shanghai |       |
+----------+-------------+------+-----+----------+-------+
3 rows in set (0.00 sec)

建表后删除默认值约束的语法格式如下:

ALTER TABLE <数据表名> CHANGE COLUMN <字段名> <字段名> <数据类型> DEFAULT NULL;
ALTER TABLE 表名称 MODIFY 字段名 数据类型 ;#删除默认值约束,也不保留非空约束
ALTER TABLE 表名称 MODIFY 字段名 数据类型 NOT NULL; #删除默认值约束,保留非空约束

修改数据表 tb_dept3,将部门位置的默认值约束删除,SQL 语句和运行结果如下所示:

mysql> ALTER TABLE tb_dept3 MODIFY location VARCHAR(50);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> DESC tb_dept3;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id       | int         | NO   | PRI | NULL    |       |
| name     | varchar(30) | YES  |     | NULL    |       |
| location | varchar(50) | YES  |     | NULL    |       |
+----------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

1.3 参照完整性

简单说明如下: 现实世界中的实体之间往往存在着某种联系,在关系模型中,实体及实体间的联系都是用 关系 来描述的,那么自然就存在着关系与关系间的引用。例如,学生实体和班级实体可以分别用下面的关系表示,其中,主码(主键)用下画线标识。

学生(学生证号,姓名,性别,生日,班级编号,备注)
班级(班级编号,班级名称,备注)

在这两个关系之间存在着属性的引用,即 学生 关系引用了 班级 关系中的主码(主键)班级编号。在两个实体间,班级编号班级 关系的主码(主键),也是 学生 关系的外部码(外键)。显然,学生 关系中 班级编号 的值必须是确实存在的班级的 班级编号,即 班级 关系中该班级的记录。也就是说,学生 关系中某个属性的取值需要参照 班级 关系的属性和值。

参照完整性规则(Referential Integrity Rule)就是定义外码(外键)和主码(主键)之间的引用规则,它是对关系间引用数据的一种限制。参照完整性的定义为:若属性(或属性组)F是基本关系R的外码,它与基本关系S的主码K相对应,则对于R中每个元组在F上的值只允许两种可能,即要么取空值(F的每个属性值均为空值),要么等于S中某个元组的主码值。其中,关系R与S可以是不同的关系,也可以是同一关系,而F与K是定义在同一个域中。

例如,在 学生 关系中每个学生的 班级编号 一项,要么取空值,表示该学生还没有分配班级;要么取值必须与 班级 关系中的某个元组的 班级编号 相同,表示这个学生分配到某个班级学习。这就是参照完整性。如果 学生 关系中某个学生的 班级编号 取值不能与 班级 关系中任何一个元组的 班级编号 值相同,表示这个学生被分配到不属于所在学校的班级学习,这与实际应用环境不相符,显然是错误的,这就需要在关系模型中定义参照完整性进行约束。

1.3.1 FOREIGN KEY 约束的作用

简而言之:限定某个表的某个字段的引用完整性。 比如:员工表的员工所在部门的选择,必须在部门表能找到对应的部分。如下图所示:
在这里插入图片描述

1.3.2 主表(父表)从表(子表)

主表(父表):被引用的表,被参考的表
从表(子表):引用别人的表,参考别人的表

员工表的员工所在部门这个字段的值要参考部门表:部门表是主表,员工表是从表。
学生表、课程表、选课表:选课表的学生和课程要分别参考学生表和课程表,学生表和课程表是主表,选课表是从表。

1.3.3 特点

  1. 从表的外键列,必须引用/参考主表的 主键或唯一约束的列。为什么?因为被依赖/被参考的值必须是唯一的。
  2. 在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名(例如:student_ibfk_1),也可以指定外键约束名。
  3. 创建(CREATE) 表时就指定外键约束的话,先创建主表,再创建从表。
  4. 删表时,先删从表(或先删除外键约束),再删除主表。
  5. 当主表的记录被从表参照时,主表的记录将不允许删除,如果要删除数据,需要先删除从表中依赖 该记录的数据,然后才可以删除主表的数据。
  6. 从表 中指定外键约束,并且一个表可以建立多个外键约束。
  7. 从表的外键列与主表被参照的列名字可以不相同,但是数据类型必须一样,逻辑意义一致。如果类型不一样 , 创建子表时 , 就会出现错误。
  8. 当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。 但是索引名是外键的约束名。(根据外键查询效率很高)
  9. 删除外键约束后,必须手动删除对应的索引。

1.3.4 添加外键约束

建表时:

# 外键语法
 CREATE TABLE 主表名称 (
  字段1 数据类型 PRIMARY KEY,
  字段2 数据类型
);

CREATE TABLE 从表名称 (
  字段1 数据类型 PRIMARY KEY,
  字段2 数据类型,
  [ CONSTRAINT < 外键约束名称 > ] FOREIGN KEY(从表的某个字段
) REFERENCES 主表名 (被参考字段));
-- #(从表的某个字段)的数据类型必须与主表名(被参考字段)的数据类型一致,逻辑意义也一样
-- #(从表的某个字段)的字段名可以与主表名(被参考字段)的字段名一样,也可以不一样
-- FOREIGN KEY: 在表级指定子表中的列
-- REFERENCES: 标示在父表中的列

示例:

 CREATE TABLE dept (#主表
  did INT PRIMARY KEY,#部门编号
   dname VARCHAR (50) #部门名称
);

#从表
CREATE TABLE emp(
eid INT PRIMARY KEY, #员工编号
ename VARCHAR(5), # 员工姓名
deptid INT, #员工所在的部门
FOREIGN KEY(deptid) REFERENCES dept(did)  # 在从表中指定外键约束
#emp表的deptid和和dept表的did的数据类型一致,意义都是表示部门的编号
);
#主表dept必须先创建成功,然后才能创建emp表,指定外键成功
#删除表时,先删除从表emp,再删除主表dept

建表后: 一般情况下,表与表的关联都是提前设计好了的,因此,会在创建表的时候就把外键约束定义好。不过,如果需要修改表的设计(比如添加新的字段,增加新的关联关系),但没有预先定义外键约束,那么,就要用修改表的方式来补充定义。

ALTER TABLE 从表名 ADD [CONSTRAINT 约束名] FOREIGN KEY (从表的字段) REFERENCES 主表名(被引用
字段) [ON UPDATE xx][ON DELETE xx];

示例:

#这两个表创建时,没有指定外键的话,那么创建顺序是随意
CREATE TABLE dept3(
did INT PRIMARY KEY, 
dname VARCHAR(50)
);

CREATE TABLE emp3(
eid INT PRIMARY KEY, #员工编号
ename VARCHAR(5),	#员工姓名
deptid INT	#员工所在的部门
); 

ALTER TABLE emp3 ADD FOREIGN KEY (deptid) REFERENCES dept(did);

演示添加、删除、修改问题:

mysql> INSERT INTO dept VALUES(1001,'教学部');
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO dept VALUES(1003, '财务部');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO emp VALUES(1,'张三',1001); #添加从表记录成功,在添加这条记录时,要求部门表有1001部门
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO emp VALUES(2,'李四',1005);#添加从表记录失败
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`mysql_study`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`deptid`) REFERENCES `dept` (`did`))
mysql> SELECT * FROM dept;
+------+--------+
| did  | dname  |
+------+--------+
| 1001 | 教学部 |
| 1003 | 财务部 |
+------+--------+
2 rows in set (0.00 sec)

mysql> SELECT * FROM emp;
+-----+-------+--------+
| eid | ename | deptid |
+-----+-------+--------+
|   1 | 张三  |   1001 |
+-----+-------+--------+
1 row in set (0.00 sec)

mysql> UPDATE dept SET did = 1002 WHERE did = 1001;#修改主表失败
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`mysql_study`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`deptid`) REFERENCES `dept` (`did`))
mysql> UPDATE dept SET did = 1002 WHERE did = 1003;#修改主表成功 因为部门表的1003部门没有被emp表引用,所以可以修改
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> DELETE FROM dept WHERE did=1001; #删除主表失败
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`mysql_study`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`deptid`) REFERENCES `dept` (`did`))

总结:约束关系是针对双方的。

添加了外键约束后,主表的修改和删除数据受约束
添加了外键约束后,从表的添加和修改数据受约束
在从表上建立外键,要求主表必须存在
删除主表时,要求从表从表先删除,或将从表中外键引用该主表的关系先删除

1.3.5 约束等级

  1. Cascade 方式 :在父表上 update/delete 记录时,同步 update/delete 掉子表的匹配记录
  2. Set null 方式 :在父表上 update/delete 记录时,将子表上匹配记录的列设为 null,但是要注意子表的外键列不能为 not null
  3. No action 方式 :如果子表中有匹配的记录,则不允许对父表对应候选键进行 update/delete 操作
  4. Restrict 方式 :同 no action, 都是立即检查外键约束
  5. Set default 方式 (在可视化工具 SQLyog 中可能显示空白):父表有变更时,子表将外键列设置成一个默认的值,但 Innodb 不能识别。ps: 如果没有指定等级,就相当于 Restrict 方式。

对于外键约束,最好是采用:演示1:on update cascade on delete set null

CREATE TABLE dept5(
did INT PRIMARY KEY, 
dname VARCHAR(50)
);

CREATE TABLE emp5(
eid INT PRIMARY KEY, #员工编号
ename VARCHAR(5),	#员工姓名
deptid INT,	#员工所在的部门
FOREIGN KEY (deptid) REFERENCES dept5(did) ON UPDATE CASCADE ON DELETE SET NULL
#把修改操作设置为级联修改等级,把删除操作设置为set null等级
);

INSERT INTO dept5 VALUES(1003, '咨询部');
INSERT INTO dept5 VALUES(1002, '财务部');
INSERT INTO dept5 VALUES(1001, '教学部');
INSERT INTO emp5 VALUES(1,'张三',1001);
INSERT INTO emp5 VALUES(2,'李四',1001);#在添加这条记录时,要求部门表有1001部门
INSERT INTO emp5 VALUES(3,'王五',1003);

#修改主表成功,从表也跟着修改,修改了主表被引用的字段1003为1004,从表的引用字段就跟着修改为1004了
UPDATE dept5 SET did = 1004 WHERE did = 1003;
SELECT * FROM emp5;

#删除主表的记录成功,从表对应的字段的值被修改为null 
DELETE FROM dept5 WHERE did = 1001; 

演示2:on update set null on delete cascade

CREATE TABLE dept6(
did INT PRIMARY KEY, dname VARCHAR(50)
);
CREATE TABLE emp6(
eid INT PRIMARY KEY, #员工编号
ename VARCHAR(5),	#员工姓名
deptid INT,	#员工所在的部门
FOREIGN KEY (deptid) REFERENCES dept6(did) ON UPDATE SET NULL ON DELETE CASCADE
#把修改操作设置为set null等级,把删除操作设置为级联删除等级
);

INSERT INTO dept6 VALUES(1001,'教学部'); 
INSERT INTO dept6 VALUES(1002, '财务部'); 
INSERT INTO dept6 VALUES(1003, '咨询部');

INSERT INTO emp6 VALUES(1,'张三',1001); #在添加这条记录时,要求部门表有1001部门
INSERT INTO emp6 VALUES(2,'李四',1001);
INSERT INTO emp6 VALUES(3,'王五',1002);

SELECT * FROM dept6;
SELECT * FROM emp6;

UPDATE dept6 SET did = 1004 WHERE did = 1002; 
#删除主表的记录成功,主表的1001行被删除了,从表相应的记录也被删除了
DELETE FROM dept6 WHERE did=1001;

1.3.6 删除外键约束

流程如下:

#(1) 第一步先查看约束名和删除外键约束
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';#查看某个表的约束名
ALTER TABLE 从表名 DROP FOREIGN KEY 外键约束名;

#(2)第二步查看索引名和删除索引。(注意,只能手动删除)
SHOW INDEX FROM 表名称; #查看某个表的索引名
ALTER TABLE 从表名 DROP INDEX 索引名;

示例:

SELECT * FROM information_schema.table_constraints WHERE table_name = 'emp6';
ALTER TABLE emp6 DROP FOREIGN KEY emp6_ibfk_1;
SHOW INDEX FROM emp6;
ALTER TABLE emp6 DROP INDEX deptid;

问题1:如果两个表之间有关系(一对一、一对多),比如:员工表和部门表(一对多),它们之间是否一定要建外键约束? 答:不是的。

问题2:建和不建外键约束有什么区别?答:建外键约束,你的操作(创建表、删除表、添加、修改、删除)会受到限制,从语法层面受到限制。例如:在员工表中不可能添加一个员工信息,它的部门的值在部门表中找不到。不建外键约束,你的操作(创建表、删除表、添加、修改、删除)不受限制,要保证数据的引用完整性 ,只能依靠程序员的自觉,或者是在 Java/Python 等程序中进行限定。例如:在员工表中,可以添加一个员工的信息,它的部门指定为一个完全不存在的部门。

问题3:那么建和不建外键约束和查询有没有关系?答:没有。

在 MySQL 里,外键约束是有成本的,需要消耗系统资源。对于大并发的 SQL 操作,有可能会不适合。比如大型网站的中央数据库,可能会因为 外键约束的系统开销而变得非常慢 。所以, MySQL 允许你不使用系统自带的外键约束,在 应用层面 完成检查数据一致性的逻辑。也就是说,即使你不用外键约束,也要想办法通过应用层面的附加逻辑,来实现外键约束的功能,确保数据的一致性。

1.3.7 阿里开发规范

【 强制 】不得使用外键与级联,一切外键概念必须在应用层解决。 说明:(概念解释)学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于 单机低并发 ,不适合 分布式 、 高并发集群;级联更新是强阻塞,存在数据库 更新风暴 的风险;外键影响数据库的 插入速度

二、面试题

1、为什么建表时,加 NOT NULL DEFAULT ''default 0

答:不想让表中出现 NULL 值

2、为什么不想要 NULL 的值。

(1) 不好比较。NULL 是一种特殊值,比较时只能用专门的 IS NULLIS NOT NULL 来比较。碰到运算符,通常返回 NULL
(2) 效率不高。影响提高索引效果。因此,我们往往在建表时 NOT NULL DEFAULT ''DEFAULT 0

3、带 AUTO_INCREMENT 约束的字段值是从1开始的吗?

MySQL 中,默认 AUTO_INCREMENT 的初始值是1,每新增一条记录,字段值自动加1。设置自增属性(AUTO_INCREMENT)的时候,还可以指定第一条插入记录的自增字段的值,这样新插入的记录的自增字段值从初始值开始递增,如在表中插入第一条记录,同时指定 id 值为5,则以后插入的记录的 id 值就会从6开始往上增加。添加主键约束时,往往需要设置字段自动增加属性。

4、并不是每个表都可以任意选择存储引擎?

外键约束(FOREIGN KEY)不能跨引擎使用。MySQL 支持多种存储引擎,每一个表都可以指定一个不同的存储引擎,需要注意的是:外键约束是用来 保证数据的参照完整性的,如果表之间需要关联外键,却指定了不同的存储引擎,那么这些表之间是不能创建外键约束的。所以说,存储引擎的选择也不完全是随意的。

小结: 本文主要介绍了定义完整性约束、命名完整性约束、删除完整性约束和修改完整性约束等内容。其中,定义完整性约束和命名完整性约束是本章的重点,需要读者认真学习、灵活掌握,这在以后的数据库设计中非常实用。

至此今天的学习就到此结束了,笔者在这里声明,笔者写文章只是为了学习交流,以及让更多学习数据库的读者少走一些弯路,节省时间,并不用做其他用途,如有侵权,联系博主删除即可。感谢您阅读本篇博文,希望本文能成为您编程路上的领航者。祝您阅读愉快!


在这里插入图片描述

    好书不厌读百回,熟读课思子自知。而我想要成为全场最靓的仔,就必须坚持通过学习来获取更多知识,用知识改变命运,用博客见证成长,用行动证明我在努力。
    如果我的博客对你有帮助、如果你喜欢我的博客内容,请 点赞评论收藏 一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
 编码不易,大家的支持就是我坚持下去的动力。点赞后不要忘了 关注 我哦!

猜你喜欢

转载自blog.csdn.net/xw1680/article/details/128848357