【MySQL】-- 数据库约束
文章目录
- 1. 什么是数据库约束
- 2. 约束类型
- 3. NOT NULL 非空约束
- 4. DEFALUT 默认值约束
- 5. UNIQUE 唯一约束
- 6. PRIMARY KEY 主键约束
- 6.1 自增主键
- 6.1 一个自增主键包含多个列
- 7. FOREIGN KEY 外键约束
- 8. CHECK 约束
1. 什么是数据库约束
数据库约束是指对数据库表中的数据所施加的规则或条件,用于确保数据的准确性和可靠性。这些约束可以是基于数据类型、值范围、唯一性、非空等规则,以确保数据的正确性和相容性。
数据库约束时关系型数据库的一个重要功能,主要的作用是保证数据的有效性,也可以理解为数据的正确性(数据本身是否正确,关联关系是否正确)。
人工检查数据的完整性工作量非常大,在数据库中定义一些约束,那么数据在写入数据库的时候,就会帮我们做一些校验。约束一般是在指定的列上创建的。
2. 约束类型
类型 | 说明 |
---|---|
NOTNULL⾮空约束 | 指定非空约束的列不能存储NULL值 |
DEFALUT默认约束 | 当没有给列赋值时使用的默认值 |
UNIQUE唯一约束 | 指定唯一约束的列每行数据必须有唯一的值 |
PRIMARYKEY主键约束 | NOTNULL和UNIQUE的结合,可以指定一个列或多个列,有助于防止数据重复和提高数据的查询性能 |
FOREIGNKEY外键约束 | 外键约束是一种关系约束,用于定义两个表之间的关联关系,可以确保数据的完整性和一致性 |
CHECK约束 | 用于限制列或数据在数据库表中的值,确保数据的准确性和可靠性 |
前四个比较常用。
3. NOT NULL 非空约束
定义表时某列不允许为null时,可以为列添加非空约束。
- 比如创建一个学生表,学生名为null时,这条记录是不完整的
-- 创建表
mysql> drop table if exists student;
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql> create table student(-> id bigint,-> name varchar(20)-> );
Query OK, 0 rows affected (0.03 sec)
-- 插入数据
mysql> insert into student values (1, null);
Query OK, 1 row affected (0.01 sec)
//查询
mysql> select * from student;
+------+------+
| id | name |
+------+------+
| 1 | NULL |
+------+------+
1 row in set (0.03 sec)
姓名为null,没有意义。
- 此时需要约束学生名的列不能为null
-- 创建表
mysql> drop table if exists student;
Query OK, 0 rows affected (0.05 sec)
-- 为name列添加非空约束
mysql> create table student(-> id bigint,-> name varchar(20) not null-> );
Query OK, 0 rows affected (0.05 sec)
-- 插入name为null的数据,会报错
mysql> insert into student (id, name) values (1, null);
ERROR 1048 (23000): Column 'name' cannot be null
-- 插入非空数据,可以正常插入
mysql> insert into student (id, name) values (1, '张三');
Query OK, 1 row affected (0.03 sec)
-- 查询数据
mysql> select * from student;
+------+------+
| id | name |
+------+------+
| 1 | 张三 |
+------+------+
1 row in set (0.00 sec)
- 查看表结构
mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | bigint | YES | | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
可以看到student表中的Null列对应的name那一行显示为NO,就表示name不能为空。
4. DEFALUT 默认值约束
DEFALUT约束用于向列中插入默认值,如果没有为列设置值,那么会将默认值设置到该列
- 创建学生表,新增年龄列,并为其设置默认值18
mysql> drop table if exists student;
Query OK, 0 rows affected (0.01 sec)-- 创建表
mysql> create table student(-> id bigint primary key auto_increment,-> name varchar(20) not null,-> age int default 18-> );
Query OK, 0 rows affected (0.02 sec)-- 查看表结构
mysql> desc student;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
| age | int | YES | | 18 | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
没有指定值的就用默认值填充
-- 添加数据
mysql> insert into student (name) values ('张三'), ('王五');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0mysql> select * from student;
+----+------+------+
| id | name | age |
+----+------+------+
| 1 | 张三 | 18 |
| 2 | 王五 | 18 |
+----+------+------+
2 rows in set (0.01 sec)
有指定值就用指定值填充
mysql> insert into student (name, age) values ('赵六', 20);
Query OK, 1 row affected (0.00 sec)mysql> select * from student;
+----+------+------+
| id | name | age |
+----+------+------+
| 1 | 张三 | 18 |
| 2 | 王五 | 18 |
| 3 | 赵六 | 20 |
+----+------+------+
3 rows in set (0.00 sec)
5. UNIQUE 唯一约束
指定了唯一约束的列,该列的值在所有记录中不能重复,比如一个人的身份证号,学生的学号等。
- 重构学生表,新增学号列
mysql> drop table if exists student;
Query OK, 0 rows affected (0.05 sec)mysql> create table student(-> id bigint,-> name varchar(20),-> sno varchar(10)-> );
Query OK, 0 rows affected (0.06 sec)
- ** 不设置唯一约束时,学号可以重复**
mysql> insert into student (id, name, sno) values (1, '张三', '123456789');
Query OK, 1 row affected (0.00 sec)mysql> insert into student (id, name, sno) values (1, '张三', '123456789');
Query OK, 1 row affected (0.00 sec)mysql> select * from student;
+------+------+-----------+
| id | name | sno |
+------+------+-----------+
| 1 | 张三 | 123456789 |
| 1 | 张三 | 123456789 |
+------+------+-----------+
2 rows in set (0.00 sec)
- ** 重构学生表,为学号列设置唯一约束**
mysql> drop table if exists student;
Query OK, 0 rows affected (0.02 sec)mysql> create table student(-> id bigint,-> name varchar(20),-> sno varchar(10) UNIQUE-> );
Query OK, 0 rows affected (0.09 sec)mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | bigint | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| sno | varchar(10) | YES | UNI | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
- 插入重复的学号时会报错,这就是唯一约束在生效
mysql> insert into student (id, name, sno) values (1, '张三', '123456789');
Query OK, 1 row affected (0.03 sec)mysql> insert into student (id, name, sno) values (1, '张三', '123456789');
ERROR 1062 (23000): Duplicate entry '123456789' for key 'student.sno'
- 加入唯一约束的列可以写入null值,且可以写入多个null值
mysql> insert into student (id, name, sno) values (1, '张三', null);
Query OK, 1 row affected (0.03 sec)mysql> insert into student (id, name, sno) values (1, '张三', null);
Query OK, 1 row affected (0.03 sec)mysql> insert into student (id, name, sno) values (1, '张三', null);
Query OK, 1 row affected (0.03 sec)mysql> select * from student;
+------+------+-----------+
| id | name | sno |
+------+------+-----------+
| 1 | 张三 | 123456789 |
| 1 | 张三 | NULL |
| 1 | 张三 | NULL |
| 1 | 张三 | NULL |
+------+------+-----------+
4 rows in set (0.00 sec)
6. PRIMARY KEY 主键约束
- 主键约束唯一 标识数据库表中的每条记录(数据库管理数据时,使用主键列作为数据行的“身份证编号”)。
- 主键必须包含唯一的值,且不能包含null值(非空约束 + 唯一约束)。
- 每个表只能有一个主键,可以由单个列或多个列组成(主键由多个列组成的是复合主键)。
- 通常每张表都只当一个主键,主键列建议使用bigint类型(范围足够大,不会溢出)。
- 重构学生表,为ID列添加非空和唯一约束
mysql> drop table if exists student;
Query OK, 0 rows affected (0.01 sec)mysql> create table student(-> id bigint not null unique,-> name varchar(20) not null-> );
Query OK, 0 rows affected (0.02 sec)mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | bigint | NO | PRI | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
在id列上加了非空约束和唯一约束,在表结构上的key那一列显示PRI。
mysql> drop table if exists student;
Query OK, 0 rows affected (0.01 sec)mysql> create table student(-> id bigint primary key,-> name varchar(20) not null-> );
Query OK, 0 rows affected (0.02 sec)mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | bigint | NO | PRI | NULL | |
| name | varchar(20) | NO | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
为id列加上主键约束,可以看到在表结构的key那一列显示PRI
非空约束 + 唯一约束 = 主键约束
6.1 自增主键
- 把表中的ID字段设置成自增主键
mysql> drop table if exists student;
Query OK, 0 rows affected (0.02 sec)mysql> create table student(-> id bigint primary key auto_increment,-> name varchar(20) not null-> );
Query OK, 0 rows affected (0.04 sec)mysql> desc student;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
auto_increment
是自增的关键字,一个表中只能由一个列是自增列。- 可以在表结构的Extra列上看到
auto_increment
。
- 可以手动指定主键列的值,但是要保证不重复
mysql> insert into student (id, name) values (1, '张三');
Query OK, 1 row affected (0.01 sec)mysql> insert into student (id, name) values (1, '李四');
ERROR 1062 (23000): Duplicate entry '1' for key 'student.PRIMARY'
mysql> insert into student (id, name) values (2, '李四');
Query OK, 1 row affected (0.03 sec)
- 主键列在手动设置时,如果设置为null,则会使用自增
mysql> insert into student (id, name) values (null, '王五');
Query OK, 1 row affected (0.01 sec)mysql> select * from student;
+----+------+
| id | name |
+----+------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
+----+------+
3 rows in set (0.01 sec)
- 插入除了主键之外的所有非空列(推荐使用这种方法插入数据)
mysql> insert into student (name) values ('赵六');
Query OK, 1 row affected (0.01 sec)mysql> select * from student;
+----+------+
| id | name |
+----+------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 赵六 |
+----+------+
4 rows in set (0.01 sec)
- 自定义的主键值,只要满足非空和唯一即可,不需要严格按照数字递增
mysql> insert into student (id, name) values (100, '哈哈');
Query OK, 1 row affected (0.01 sec)mysql> select * from student;
+-----+------+
| id | name |
+-----+------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 赵六 |
| 100 | 哈哈 |
+-----+------+
5 rows in set (0.00 sec)mysql> insert into student (id, name) values (99, '哈哈');
Query OK, 1 row affected (0.01 sec)mysql> select * from student;
+-----+------+
| id | name |
+-----+------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 赵六 |
| 99 | 哈哈 |
| 100 | 哈哈 |
+-----+------+
6 rows in set (0.00 sec)mysql> insert into student (name) values ('哈哈2');
Query OK, 1 row affected (0.01 sec)mysql> select * from student;
+-----+-------+
| id | name |
+-----+-------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 赵六 |
| 99 | 哈哈 |
| 100 | 哈哈 |
| 101 | 哈哈2 |
+-----+-------+
7 rows in set (0.00 sec)
6.1 一个自增主键包含多个列
mysql> drop table if exists student;
Query OK, 0 rows affected (0.01 sec)mysql> create table student(-> id bigint,-> name varchar(20),-> primary key (id, name)-> );
Query OK, 0 rows affected (0.02 sec)mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | bigint | NO | PRI | NULL | |
| name | varchar(20) | NO | PRI | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
- 如果定义的主键包含多个列,那么表示的是多个列的值用-连起来的值,不能重复
mysql> insert into student (id, name) values (1, '张三');
Query OK, 1 row affected (0.01 sec)mysql> insert into student (id, name) values (2, '张三');
Query OK, 1 row affected (0.00 sec)mysql> select * from student;
+----+------+
| id | name |
+----+------+
| 1 | 张三 |
| 2 | 张三 |
+----+------+
2 rows in set (0.00 sec)mysql> insert into student (id, name) values (2, '张三');
ERROR 1062 (23000): Duplicate entry '2-张三' for key 'student.PRIMARY'
7. FOREIGN KEY 外键约束
- 外键约束用于定义主表和从表之间的关系。
- 外键约束定义在从表的列上,主表关联的列必须是主键或唯一约束。
- 当定义外键后,要求从表中的外键列数据必须在主表的主键或唯一列存在或为null.
外键约束也是对数据的一个校验过程,从表中使用主表中的某个值,这个值必须在主表中存在。
语法
foreign key (class_id) references class(id)
- 创建班级表(主表),并初始化数据
mysql> drop table if exists class;
Query OK, 0 rows affected, 1 warning (0.04 sec)mysql> create table class(-> id bigint primary key auto_increment,-> name varchar(20) not null-> );
Query OK, 0 rows affected (0.05 sec)mysql> insert into class (name) values ('java01'), ('java02'), ('java03'), ('C++01'), ('C++02');
Query OK, 5 rows affected (0.03 sec)
Records: 5 Duplicates: 0 Warnings: 0mysql> select * from class;
+----+--------+
| id | name |
+----+--------+
| 1 | java01 |
| 2 | java02 |
| 3 | java03 |
| 4 | C++01 |
| 5 | C++02 |
+----+--------+
5 rows in set (0.02 sec
- 创建学生表,加入外键约束
mysql> drop table if exists student;
Query OK, 0 rows affected (0.03 sec)mysql> create table student(-> id bigint primary key auto_increment,-> name varchar(20) not null,-> sno varchar(20) unique,-> class_id bigint not null,-> foreign key (class_id) references class (id)-> );
Query OK, 0 rows affected (0.04 sec)mysql> desc student;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
| sno | varchar(20) | YES | UNI | NULL | |
| class_id | bigint | NO | MUL | NULL | |
+----------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
- 插入一个班级编号在主表中不存在的一组数据
mysql> insert into student(name, sno, class_id) values ('qianqi', '1007', 6);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`java114`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))
- 删除主表中的数据
mysql> select * from student;
+----+------+-------+----------+
| id | name | sno | class_id |
+----+------+-------+----------+
| 5 | 张三 | 10001 | 1 |
| 6 | 王五 | 10002 | 3 |
| 7 | 赵六 | 10003 | 5 |
+----+------+-------+----------+
3 rows in set (0.00 sec)mysql> select * from class;
+----+--------+
| id | name |
+----+--------+
| 1 | java01 |
| 3 | java03 |
| 4 | C++01 |
| 5 | C++02 |
+----+--------+
4 rows in set (0.00 sec)mysql> delete from class where id = 3;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`java114`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`))
删除主表中的数据时,如果从表中有对主表的引用,则不允许删除主表的记录。
mysql> select * from student;
+----+------+-------+----------+
| id | name | sno | class_id |
+----+------+-------+----------+
| 5 | 张三 | 10001 | 1 |
| 6 | 王五 | 10002 | 3 |
| 7 | 赵六 | 10003 | 5 |
+----+------+-------+----------+
3 rows in set (0.00 sec)mysql> select * from class;
+----+--------+
| id | name |
+----+--------+
| 1 | java01 |
| 3 | java03 |
| 4 | C++01 |
| 5 | C++02 |
+----+--------+
4 rows in set (0.00 sec)mysql> delete from student where class_id = 3;
Query OK, 1 row affected (0.01 sec)mysql> select * from class;
+----+--------+
| id | name |
+----+--------+
| 1 | java01 |
| 3 | java03 |
| 4 | C++01 |
| 5 | C++02 |
+----+--------+
4 rows in set (0.00 sec)mysql> select * from student;
+----+------+-------+----------+
| id | name | sno | class_id |
+----+------+-------+----------+
| 5 | 张三 | 10001 | 1 |
| 7 | 赵六 | 10003 | 5 |
+----+------+-------+----------+
2 rows in set (0.00 sec)
如果要删除主表中的数据,必须先把从表中对主表的引用记录删除掉。
mysql> drop table class;
ERROR 3730 (HY000): Cannot drop table 'class' referenced by a foreign key constraint 'student_ibfk_1' on table 'student'.
删除主表时,必须先解除主外键关系或者先删除从表。
每当在有主外键约束的表中新增一条数据时,数据库都会为我们做校验,数据量非常大的时候,会严重影响数据库的效率,在真正的工作中,一般不会为表加主外键约束。
数据库的校验操作一般是在应用程序层面处理好,再把正确的数据直接写到数据库中。
8. CHECK 约束
可以应用于一个或多个列,用于限制列中可接收的数据值,从而保证数据的完整性和准确性。
在8.0.16开始全面支持CHECK约束,之前的版本会忽略CHECK约束。
但是在工作中,一般是在应用程序级别进行校验的。
- 重构学生表,有以下要求,年龄不能小于16岁,性别只能是男或女
mysql> drop table if exists student;
Query OK, 0 rows affected (0.01 sec)mysql> create table student(-> id bigint primary key auto_increment,-> name varchar(20) not null,-> age int default 18,-> gender char(1),-> check(age >= 16),-> check(gender = '男' or gender = '女')-> );
Query OK, 0 rows affected (0.03 sec)mysql> desc student;
+--------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
| age | int | YES | | 18 | |
| gender | char(1) | YES | | NULL | |
+--------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
- 符合check条件的插入
mysql> insert into student (name, age, gender) values ('张三', 18, '男');
Query OK, 1 row affected (0.03 sec)mysql> select * from student;
+----+------+------+--------+
| id | name | age | gender |
+----+------+------+--------+
| 1 | 张三 | 18 | 男 |
+----+------+------+--------+
1 row in set (0.00 sec)
- 不符合check条件(age)的插入
mysql> insert into student (name, age, gender) values ('张三', 15, '男');
ERROR 3819 (HY000): Check constraint 'student_chk_1' is violated.
- 不符合check条件(gender)的插入
insert into student (name, age, gender) values ('王五', 18, '好');
ERROR 3819 (HY000): Check constraint 'student_chk_2' is violated.
- ** 都不符合**
mysql> insert into student (name, age, gender) values ('赵六', 8, '好');
ERROR 3819 (HY000): Check constraint 'student_chk_1' is violated.