MySQL中的约束


精选专栏链接 🔗


欢迎订阅,点赞+关注,每日精进1%,与百万开发者共攀技术珠峰

更多内容持续更新中!希望能给大家带来帮助~ 😀😀😀


MySQL中的约束


1,数据完整性

数据完整性指数据的准确性和可靠性。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。

为保证数据的完整性,SQL规范以约束的方式对表数据进行额外的条件限制。我们从以下四个方面举例:

  • 实体完整性:例如,同一个表中,不能存在两条完全相同无法区分的记录;
  • 域完整性:例如:年龄范围 0-120,性别范围 "男/女" ;
  • 引用完整性:例如:员工所在的部门,要在在部门表中能找到;
  • 用户自定义完整性:例如:用户名唯一、密码不能为空、本部门经理的工资不得高于本部门职工的平均工资的5倍;

2,约束

MySQL 中的约束用于限制表中数据的规则,以确保数据的准确性和可靠性。


2.1,约束的分类

按照不同的角度可将约束分为多种不同的类别:

根据约束数据列是一列还是多列, 约束可分为:单列约束多列约束

根据约束的作用范围,约束可分为:

  • 列级约束:只能作用在一个列上,声明时跟在字段声明的后面;
  • 表级约束:可以作用在多个列上,表中所有字段声明完后,在后面声明约束;

根据约束起的作用或功能,约束可分为:

  • NOT NULL非空约束,规定某个字段不能为空
  • UNIQUE唯一性约束规定某个字段在整个表中是唯一的
  • PRIMARY KEY主键(非空且唯一)约束
  • FOREIGN KEY外键约束
  • CHECK检查约束
  • DEFAULT默认值约束

添加约束时,可以在创建表时规定约束(通过 CREATE TABLE 语句);也可在表创建之后规定约束或删除约束(通过 ALTER TABLE 语句);


2.2,查看表中已有约束

方式一:图形化界面查看

如下索引栏下面呈现的也是一些约束。

方式二:通过SQL语句查看约束

sql 复制代码
# information_schema是数据库名(系统自带的库)
# table_constraints是information_schema数据库中的一个表的名称(专门存储各个表的约束)
SELECT * FROM information_schema.table_constraints 
WHERE table_name = '表名称';

使用如上SQL代码,查询查询 employees 表的约束,运行结果如下:


3,不同约束的使用

接下来结合具体的例子讲解不同约束的使用。


3.1,非空约束:NOT NULL

非空约束用来限定某个字段(即某列)的值不为空。关键字是NOT NULL。非空约束的特点如下:

  • 默认情况下,所有的类型的值都为NULL,只有显式加上非空约束才能让字段赋值时不为NULL;

  • 非空约束只能出现在表对象的列上,只能针对某个列单独限定非空,不能组合非空;

  • 一个表可以有很多列都分别限定了非空

  • 空字符串不等于NULL,0也不等于NULL;

建表时添加非空约束

创建测试表test,并规定 id 和 NAME 不为空

sql 复制代码
CREATE TABLE test(
id INT(10) NOT NULL,
NAME VARCHAR(20) NOT NULL,
sex CHAR
);

创建成功后,查看表结构:

sql 复制代码
DESC test;

结果如下:

此后,如果执行的插入或修改表的语句中没有给 id 或 NAME 字段正确赋值,就会报错:

sql 复制代码
insert into test values(1,NULL,'男'); 
sql 复制代码
UPDATE test 
SET NAME = NULL
WHERE id = 1;

修改表时添加非空约束

修改测试表test,并规定 sex 不能为空:

sql 复制代码
ALTER TABLE test 
MODIFY sex VARCHAR(25) NOT NULL;

注意:执行上述SQL语句给 sex 属性添加非空约束前需要先确定表内所有数据的 sex 列均不为NULL,否则会报错。

删除非空约束

修改测试表test,并删除 sex 字段的非空约束:

sql 复制代码
ALTER TABLE test
MODIFY sex VARCHAR(25) NULL;

查看表结构:

sql 复制代码
DESC test;

运行结果如下:


3.2,唯一性约束:UNIQUE

唯一性约束用来限制某个字段(即某列)的值不能重复。关键字是UNIQUE。唯一性约束的特点如下:

  • 同一个表可以有多个唯一约束;
  • 唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一;
  • 唯一性约束允许列值为 NULL,而且可以多次添加 NULL 值;
  • 在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同;

创建表时添加唯一性约束

给 id 设置列级约束,email 设置表级唯一性约束:

sql 复制代码
CREATE TABLE test2(
id INT UNIQUE, #列级约束
last_name VARCHAR(15) ,
email VARCHAR(25),
salary DECIMAL(10,2), # 注意不要忘记此处的逗号
# 表级唯一性约束,约束名uk_test2_email
CONSTRAINT uk_test2_email UNIQUE(email)
);

或者不手动给表级约束起名,即更简洁的写法如下:

sql 复制代码
CREATE TABLE test2(
id INT UNIQUE, #列级约束
last_name VARCHAR(15) ,
email VARCHAR(25),
salary DECIMAL(10,2),  # 注意不要忘记此处的逗号
# 表级唯一性约束
UNIQUE(email)
);

创建成功后,查看 test2 表结构:

sql 复制代码
DESC test2;

运行结果如下:

执行如下代码,查看 test2 表内已有约束:

sql 复制代码
SELECT * FROM information_schema.table_constraints 
WHERE table_name = 'test2';

运行结果如下:

给字段添加唯一性约束后,这些次段的数据不唯一就会报错。

唯一性约束允许列值为空,多个 NULL 值不会违反唯一性约束

代码演示如下:

sql 复制代码
INSERT INTO test2(id, last_name, email, salary)
VALUES
  (2, 'Tom1', NULL, 4600),
  (3, 'Tom2', NULL, 4600);

执行上述代码可以插入数据成功,然后查看表内数据:

修改表时添加唯一性约束

给表 test2 的 salary 字段和 last_name 字段添加唯一性约束:

sql 复制代码
# 方式一:类似表级约束
ALTER TABLE test2
ADD CONSTRAINT uk_test2_sal UNIQUE(salary);

# 方式二:类似列级约束
ALTER TABLE test2
MODIFY last_name VARCHAR(15) UNIQUE;

复合唯一性约束(多列)

有学生表、课程表、选课表三个表。那么可以在选课表中的学生id课程id两个字段上,形成一个复合唯一性约束。具体建表语句如下:

sql 复制代码
#学生表
CREATE TABLE student(
    sid INT,	#学号
    sname VARCHAR(20),	#姓名
    tel CHAR(11) UNIQUE KEY,  #电话
    cardid CHAR(18) UNIQUE KEY #身份证号
);

#课程表
CREATE TABLE course(
    cid INT,  #课程编号
    cname VARCHAR(20)     #课程名称
);

#选课表
CREATE TABLE student_course(
    id INT,
    sid INT,  #学号
    cid INT,  #课程编号
    score INT,
    UNIQUE KEY(sid,cid)  #复合唯一
);

删除唯一性约束

  • 添加唯一性约束的列上也会自动创建唯一索引;
  • 删除唯一约束只能通过删除唯一索引的方式删除;
  • 删除唯一索引时需要指定唯一索引名,唯一索引名和唯一约束名一样;
  • 创建唯一约束时未指定名称的情况下,如果是单列,就默认和列名相同;如果是组合列,那么默认和 () 中排在第一个的列名相同。也可以自定义唯一性约束名;(比如上面提到的选课表中的个唯一约束名称为 sid)

查询 test2 表的约束

需求:删除掉 test2 表中 email 字段的唯一性约束。SQL示例如下:

sql 复制代码
ALTER TABLE test2
DROP INDEX uk_test2_email;  # 根据唯一索引名或唯一约束名删除索引

3.3,主键约束:PRIMARY KEY

主键约束用来唯一标识表中的一行记录。主键约束相当于唯一约束---+非空约束的组合主键约束列不允许重复,也不允许出现空值。 其特点和要求如下:

  • 一个表最多只能有一个主键约束,建立主键约束可以在列级别创建,也可以在表级别上创建;
  • 主键约束对应着表中的一列或者多列(复合主键);

  • 如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复;

  • MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用;

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

  • 需要注意的一点是,一般不会修改主键字段的值。因为主键是数据记录的唯一标识,如果修改了主键的值,就有可能会破坏数据的完整性;

创建表时添加主键约束

方式一:列级主键约束

sql 复制代码
CREATE TABLE test1(
id INT PRIMARY KEY, #列级
last_name VARCHAR(15),
salary DECIMAL(10,2),
email VARCHAR(25)
);

方式二:表级主键约束

sql 复制代码
CREATE TABLE test2(
id INT , 
last_name VARCHAR(15),
salary DECIMAL(10,2),
email VARCHAR(25),
#表级
CONSTRAINT pk_test5_id PRIMARY KEY(id)  #不用起名
);

添加主键约束后,如果插入的数据的主键重复或者为空则会报错,如下图所示 :

重复添加时:

插入主键为空的数据时:

建表时添加复合型主键约束

NAME字段和PASSWORD字段组成复合主键约束

sql 复制代码
CREATE TABLE user1(
id INT,
NAME VARCHAR(15),
PASSWORD VARCHAR(25),
PRIMARY KEY (NAME,PASSWORD)
);

注意:复合型主键约束要求,复合起来的多个字段都不能有NULL值,并且组合的值不允许重复。

修改表的时候添加主键约束

新建一个 test3 表 :

sql 复制代码
CREATE TABLE test3(
id INT ,
last_name VARCHAR(15),
salary DECIMAL(10,2),
email VARCHAR(25)
);

给 test3 表的 id 列增加主键约束:

sql 复制代码
ALTER TABLE test3
ADD PRIMARY KEY (id);

查看已添加的约束:

sql 复制代码
DESC test3;

运行结果如下:

删除主键约束(实际开发基本不会这样做)

sql 复制代码
ALTER TABLE test3
DROP PRIMARY KEY;

查看主键约束是否已删除:


3.4,检查约束:CHECK

CHECK 约束用来检查某个字段的值是否符合某些要求。

注意:MySQL 5.7 可以使用check约束,添加数据时,没有任何错误或警告,但check不会起作用。在MySQL 8.0中可以正常使用check约束了
添加 check 约束

sql 复制代码
CREATE TABLE test10(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2) CHECK(salary > 2000)
);

此时插入如下数据,由于薪资2500大于2000满足check,所以可以插入成功:

sql 复制代码
INSERT INTO test10
VALUES(1,'Tom',2500);

此时插入如下数据,由于薪资1200小于2000不满足check,所以插入失败:

sql 复制代码
INSERT INTO test10
VALUES(2,'Bob',1200);

3.5,默认值约束:DEFAULT

DEFAULT约束用来给某个字段或某列指定默认值,一旦设置默认值,在插入数据时,如果此字段没有显式赋值,则被赋值为默认值。

建表时给字段添加默认值约束

sql 复制代码
CREATE TABLE test11(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2) DEFAULT 2000
);

此时查看表结构,发现默认值设置成功:

sql 复制代码
DESC test11;

此时插入如下数据时,由于显式指定了 salary 为3000 则会赋值为3000:

sql 复制代码
INSERT INTO test11(id,last_name,salary)
VALUES(1,'Tom',3000);

若插入如下数据时,由于没有显式指定了 salary ,则会给 salary 赋值为设置的默认值2000:

sql 复制代码
INSERT INTO test11(id,last_name)
VALUES(2,'Tom1');

修改表时给字段添加默认值约束

sql 复制代码
# 建表
CREATE TABLE test12(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10,2)
);

# 修改表,添加默认值约束
ALTER TABLE test12
MODIFY salary DECIMAL(8,2) DEFAULT 2500;

修改表时给字段删除默认值约束

sql 复制代码
ALTER TABLE test12
MODIFY salary DECIMAL(8,2);

4,自增列:AUTO_INCREMENT

AUTO_INCREMENT可以实现某个字段的值自动增长。其特点和要求如下:

  • 一个表最多只能有一个自增长列;
  • 当需要产生唯一标识符或顺序值时,可设置自增长;
  • 自增长列约束的列必须是主键列或唯一键列,否则会报错;
  • 自增约束的列的数据类型必须是整数类型

创建表时设置字段自增

sql 复制代码
CREATE TABLE test7(
id INT PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(15) 
);

注意:

  • 如果自增列指定了 0 和 null,会在当前表自增字段最大值的基础上自增;如果自增列手动指定了具体值,则直接赋值为具体值,随后再插入数据时会在指定值的基础上自增;
  • 开发中,一旦主键作用的字段上声明有AUTO_INCREMENT,则我们在添加数据时,就可以不要给主键
    对应的字段去赋值了;

修改表时设置字段自增

创建表

sql 复制代码
CREATE TABLE test8(
id INT PRIMARY KEY ,  # 已有主键约束
last_name VARCHAR(15) 
);

给已有表设置主键字段自增

sql 复制代码
ALTER TABLE test8
MODIFY id INT AUTO_INCREMENT;

4.1,MySQL 8.0 中自增变量的持久化新特性

在MySQL 8.0之前,自增主键AUTO_INCREMENT的值如果大于 max(primary key)+1 ,在MySQL重启后,会重置 AUTO_INCREMENT=max(primary key)+1 。而这种现象被称为 "自增ID回溯" 或 "ID复用" 。在某些情况下可能导致业务主键冲突或者其他难以发现的问题。下面通过案例来对比不同的版本中自增变量是否持久化。


4.2,问题演示

在MySQL 5.7中创建表 test1 做演示:

sql 复制代码
CREATE TABLE test1(
id INT PRIMARY KEY AUTO_INCREMENT
);

插入四条数据,数据会依次递增:

sql 复制代码
INSERT INTO test9
VALUES(0),(0),(0),(0);

插入后表中数据如下所示:

此时删掉 id 为4的一条记录后再插入一条记录:

sql 复制代码
DELETE FROM test9
WHERE id = 4;

INSERT INTO test9
VALUES(0);

表中记录如下所示,新插入的数据依然是在4的基础上增加:

此时如果删除 id 为5的数据,并重启MySQL 服务(5.7版本的MySQL):

sql 复制代码
DELETE FROM test9
WHERE id = 5;

重启MySQL服务:

再次执行插入:

sql 复制代码
INSERT INTO test9
VALUES(0);

此时查看表,发现数据是从当前表 id 最大值的基础上自增,即新增数据的 id 为4:

原因是: 在MySQL 5.7系统中,对于自增主键的分配规则,是由InnoDB数据字典内部一个计数器来决定的,而该计数器只在内存中维护,并不会持久化到磁盘中。当数据库重启时,该计数器会被初始化。再添加值时,MySQL会先获取表中字段现存的最大值,在此基础上进行自增。


4.3,MySQL 8.0 中的自增

而MySQL 8.0将自增主键的计数器持久化到重做日志中。每次计数器发生改变,都会将其写入重做日志中。如果数据库重启,InnoDB会根据重做日志中的信息来初始化计数器的内存值。

sql 复制代码
#在MySQL 8.0中演示
CREATE TABLE test9(
id INT PRIMARY KEY AUTO_INCREMENT
);

INSERT INTO test9
VALUES(0),(0),(0),(0);

SELECT * FROM test9;

DELETE FROM test9
WHERE id = 4;

INSERT INTO test9
VALUES(0);

DELETE FROM test9
WHERE id = 5;

#重启服务器

SELECT * FROM test9;

INSERT INTO test9
VALUES(0);

上述流程在MySQL8.0中不会出现上述"自增ID回溯" 或 "ID复用"现象。代码运行结果如下:


相关推荐
wxin_VXbishe1 小时前
springboot新能源车充电站管理系统小程序-计算机毕业设计源码29213
java·c++·spring boot·python·spring·django·php
程序员陆通1 小时前
月烧 400 刀到不到 20 刀:我是怎么把 OpenClaw 的 Token 账单砍掉 95% 的
java·前端·数据库
Shan12051 小时前
站在计算机领域视角看:SQL注入攻击
网络·数据库·sql
轻刀快马2 小时前
别干背八股文了:从一场“双十一秒杀”惨案,看懂 InnoDB 事务、锁与索引的底层齿轮
数据库·sql
万事大吉CC2 小时前
【1】Django 基础:MTV 架构与核心组件
数据库·架构·django
曾凡宇先生2 小时前
mysql局域网授权
数据库·mysql
代码漫谈2 小时前
一文学习 SpringBoot 的 application.yml 配置,基于 Spring Boot 3.2.x
java·spring boot·spring·配置文件
SamDeepThinking2 小时前
程序员如何接受工作内容毫无意义?
java·后端·程序员
三翼鸟数字化技术团队2 小时前
基于Redis ZSet实现分布式优先级队列的技术实践
java·redis