约束概述
为什么需要约束
数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。
为了保证数据的完整性,SQL规范以约束的方式对表数据进行额外的条件限制。从以下四个方面考虑:
-
实体完整性(Entity Integrity):例如,同一个表中,不能存在两条完全相同无法区分的记录。
-
域完整性(Domain Integrity):例如:年龄范围0-120,性别范围“男/女”。
-
引用完整性(Referential Integrity):例如:员工所在部门,在部门表中要能找到这个部门。
-
用户自定义完整性(User-defined Integrity):例如:用户名唯一、密码不能为空等,本部门经理的工资不得高于本部门职工的平均工资的5倍。
约束概念
约束(constraint)是表级的强制规定。一种限制,用于限制表中的数据,为了保证表中的数据的准确和可靠性。
可以在创建表时规定约束(通过 CREATE TABLE 语句),或者在表创建之后通过 ALTER TABLE 语句规定约束。
约束的分类
根据约束数据列的限制,约束可分为:
单列约束:每个约束只约束一列
多列约束:每个约束可约束多列数据
根据约束的作用范围,约束可分为:
列级约束:只能作用在一个列上,跟在列的定义后面
表级约束:可以作用在多个列上,不与列一起,而是单独定义
位置 支持的约束类型 是否可以起约束名
列级约束: 列的后面 语法都支持,但外键没有效果 不可以
表级约束: 所有列的下面 默认和非空不支持,其他支持 可以(主键没有效果)
根据约束起的作用,约束可分为:
NOT NULL 非空约束,用于保证该字段的值不能为空。比如姓名、学号等。
UNIQUE 唯一约束,用于保证该字段的值具有唯一性,可以为空。比如座位号。
PRIMARY KEY 主键(非空且唯一)约束。比如学号、员工编号等。
FOREIGN KEY 外键约束,用于限制两个表的关系,
用于保证该字段的值必须来自于主表的关联列的值,
在从表添加外键约束,用于引用主表中某列的值。
比如学生表的专业编号,员工表的部门编号,员工表的工种编号。
CHECK 检查约束
【mysql中不支持,但可以使用,而没有任何效果】。比如年龄、性别。
DEFAULT 默认值约束,用于保证该字段有默认值。比如性别。
# 查看某个表已有的约束
#information_schema数据库名(系统库)
#table_constraints表名称(专门存储各个表的约束)
SELECT * FROM information_schema.table_constraints
WHERE table_name = '表名称';
约束的操作
/*
添加约束的时机:1.创建表时;2.修改表时
约束的添加分类:
列级约束:
六大约束语法上都支持,但外键约束没有效果
表级约束:
除了非空、默认,其他的都支持
主键和唯一的大对比:
保证唯一性 是否允许为空 一个表中可以有多少个 是否允许组合
主键 √ × 至多有1个 √,但不推荐
唯一 √ √ 可以有多个 √,但不推荐
主键:
1、一个表最多只能有一个主键约束,建立主键约束可以在列级别创建,也可以在表级别上创建。
2、主键约束对应着表中的一列或者多列(复合主键)
3、如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复。
4、MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用。
5、当创建主键约束时,系统默认会在所在的列或列组合上建立对应的主键索引
(能够根据主键查询
的,就根据主键查询,效率更高)。
如果删除主键约束了,主键约束对应的索引就自动删除了。
6、需要注意的一点是,不要修改主键字段的值。因为主键是数据记录的唯一标识,
如果修改了主键的
值,就有可能会破坏数据的完整性。
外键:
1、要求在从表设置外键关系
2、从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求
3、主表的关联列必须是一个key(一般是主键或唯一)
4、插入数据时,先插入主表,再插入从表
删除数据时,先删除从表,再删除主表
5、当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。
但是索引名是外键的约束
名。(根据外键查询效率很高)
6、删除外键约束后,必须手动删除对应的索引
*/
CREATE TABLE 表名(
字段名 字段类型 列级约束,
字段名 字段类型,
表级约束
)
CREATE DATABASE students;
#一、创建表时添加约束
#1.添加列级约束
/*
语法:
直接在字段名和类型后面追加 约束类型即可。
只支持:默认、非空、主键、唯一
*/
USE students;
DROP TABLE stuinfo;
CREATE TABLE stuinfo(
id INT PRIMARY KEY,#主键
stuName VARCHAR(20) NOT NULL UNIQUE,#非空
gender CHAR(1) CHECK(gender='男' OR gender ='女'),#检查
seat INT UNIQUE,#唯一
age INT DEFAULT 18,#默认约束
majorId INT REFERENCES major(id)#外键
);
CREATE TABLE major(
id INT PRIMARY KEY,
majorName VARCHAR(20)
);
#查看stuinfo中的所有索引,包括主键、外键、唯一
SHOW INDEX FROM stuinfo;
#2.添加表级约束
/*
语法:在各个字段的最下面
【constraint 约束名】 约束类型(字段名)
*/
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
id INT,
stuname VARCHAR(20),
gender CHAR(1),
seat INT,
age INT,
majorid INT,
[CONSTRAINT pk] PRIMARY KEY(id,stuname), #主键
CONSTRAINT uq UNIQUE(seat),#唯一键
CONSTRAINT ck CHECK(gender ='男' OR gender = '女'),#检查
#在从表中添加外键(外键为主表的主键)
[CONSTRAINT fk_stuinfo_major] FOREIGN KEY(majorid) REFERENCES major(id)#外键
);
SHOW INDEX FROM stuinfo;
#通用的写法:★
CREATE TABLE IF NOT EXISTS stuinfo(
id INT PRIMARY KEY,
stuname VARCHAR(20),
sex CHAR(1),
age INT DEFAULT 18,
seat INT UNIQUE,
majorid INT,
CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)
);
#二、修改表时添加约束
/*
1、添加列级约束
alter table 表名 modify column 字段名 字段类型 新约束;
2、添加表级约束
alter table 表名 add 【constraint 约束名】 约束类型(字段名) 【外键的引用】;
*/
DROP TABLE IF EXISTS stuinfo;
CREATE TABLE stuinfo(
id INT,
stuname VARCHAR(20),
gender CHAR(1),
seat INT,
age INT,
majorid INT
)
DESC stuinfo;
#1.添加非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NOT NULL;
#2.添加默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT DEFAULT 18;
#3.添加主键
#①列级约束
ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
#②表级约束
ALTER TABLE stuinfo ADD PRIMARY KEY(id);
#4.添加唯一
#①列级约束
ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;
#②表级约束
ALTER TABLE stuinfo ADD UNIQUE(seat);
#5.添加外键
ALTER TABLE stuinfo ADD [CONSTRAINT fk_stuinfo_major] FOREIGN KEY(majorid) REFERENCES major(id);
#三、修改表时删除约束
#1.删除非空约束
ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NULL;
#2.删除默认约束
ALTER TABLE stuinfo MODIFY COLUMN age INT ;
#3.删除主键,不需要指定主键名,因为一个表只有一个主键,删除主键约束后,非空还存
在
ALTER TABLE stuinfo DROP PRIMARY KEY;
#4.删除唯一(生成唯一约束时会生成唯一索引,删除唯一索引时会删除唯一约束)
ALTER TABLE stuinfo DROP INDEX seat;
#5.删除外键
(1)第一步先查看约束名和删除外键约束
#查看某个
表的约束名
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';
ALTER TABLE 从表名 DROP FOREIGN KEY 外键约束名;
(2)第二步查看索引名和删除索引。(注意,只能手动删除)
SHOW INDEX FROM 表名称; #查看某个表的索引名
ALTER TABLE 从表名 DROP INDEX 索引名;
ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;
SHOW INDEX FROM stuinfo;
ALTER TABLE stuinfo DROP INDEX 索引名;
开发场景
问题1:如果两个表之间有关系(一对一、一对多),比如:员工表和部门表(一对多),它们之间是否一定要建外键约束?答:不是的。
问题2:建和不建外键约束有什么区别?
答:建外键约束,你的操作(创建表、删除表、添加、修改、删除)会受到限制,从语法层面受到限制。例如:在员工表中不可能添加一个员工信息,它的部门的值在部门表中找不到。
不建外键约束,你的操作(创建表、删除表、添加、修改、删除)不受限制,要保证数据的引用完整性,只能依靠程序员的自觉,或者是在Java程序中进行限定。例如:在员工表中,可以添加一个员工的信息,它的部门指定为一个完全不存在的部门。
问题3:那么建和不建外键约束和查询有没有关系?答:没有。
在 MySQL 里,外键约束是有成本的,需要消耗系统资源。对于大并发的 SQL 操作,有可能会不适合。比如大型网站的中央数据库,可能会因为外键约束的系统开销而变得非常慢 。所以, MySQL允许你不使用系统自带的外键约束,在应用层面完成检查数据一致性的逻辑。也就是说,即使你不用外键约束,也要想办法通过应用层面的附加逻辑,来实现外键约束的功能,确保数据的一致性。
阿里开发规范
【 强制 】不得使用外键与级联,一切外键概念必须在应用层解决。说明:(概念解释)学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发 ,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
约束等级
Cascade方式:在父表上update/delete记录时,同步update/delete掉子表的匹配记录。
Set null方式:在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子
表的外键列不能为not null。
No action方式:如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作。
Restrict方式:同no action,都是立即检查外键约束。
Set default方式(在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置
成一个默认的值,但Innodb不能识别。
如果没有指定等级,就相当于Restrict方式。对于外键约束,最好是采用:ON UPDATE CASCADE ON DELETE RESTRICT
的方式。
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) on update cascade on delete set null
#把修改操作设置为级联修改等级,把删除操作设置为set null等级
);
标识列
/*
又称为自增长列
含义:可以不用手动的插入值,系统提供默认的序列值
特点:
1、标识列必须和主键搭配吗?不一定,但要求是一个key,一般声明主键自增
2、一个表可以有几个标识列?至多一个!
3、标识列的类型只能是数值型
4、标识列可以通过 SET auto_increment_increment=3;设置步长
可以通过 手动插入值,设置起始值
*/
#一、创建表时设置标识列
DROP TABLE IF EXISTS tab_identity;
CREATE TABLE tab_identity(
id INT ,
NAME FLOAT UNIQUE AUTO_INCREMENT,
seat INT
);
TRUNCATE TABLE tab_identity;
INSERT INTO tab_identity(id,NAME) VALUES(NULL,'john');
INSERT INTO tab_identity(NAME) VALUES('lucy');
SELECT * FROM tab_identity;
SHOW VARIABLES LIKE '%auto_increment%';
SET auto_increment_increment=3;
#二、建表后设置
alter table 表名称 modify 字段名 数据类型 auto_increment;
alter table tab_identity modify `NAME` int auto_increment;
#删除自增约束
#alter table 表名称 modify 字段名 数据类型 auto_increment;#增加自增约束
alter table 表名称 modify 字段名 数据类型; #去掉auto_increment相当于删除
MySQL8.0新特性:自增变量的持久化
在MySQL 8.0之前,自增主键AUTO_INCREMENT的值如果大于max(primary key)+1,在MySQL重启后,会重置AUTO_INCREMENT=max(primary key)+1
,这种现象在某些情况下会导致业务主键冲突或者其他难以发现的问题。
MySQL 8.0将自增主键的计数器持久化到 重做日志 中。每次计数器发生改变,都会将其写入重做日志中。如果数据库重启,InnoDB会根据重做日志中的信息来初始化计数器的内存值。
面试
1、为什么建表时,加 not null default '' 或 default 0
答:不想让表中出现null值。
2、为什么不想要null的值
答:(1)不好比较。null是特殊值,比较时只能用专门的is null和is 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支持多种存储引擎,每一个表都可以指定一个不同的存储引擎,
需要注意的是:外键约束是用来保证数据的参照完整性的,如果表之间需要关联外键,
却指定了不同的存储引擎,那么这些表之间是不能创建外键约束的。所以说,存储引擎的选择也不完全是随意的。
评论区