constraint
)概述为什么需要约束
数据完整性(Data Integrity
)是指数据的精确性(Accuracy
)和可靠性(Reliability
)。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。
为了保证数据的完整性,SQL
规范以约束的方式对表数据进行额外的条件限制。
Entity Integrity
)Domain Integrity
)Referential Integrity
)User-defined Integrity
)什么是约束
约束是表级的强制规定。可以在创建表时规定约束(通过CREATE TABLE
语句),或者在表创建之后通过ALTER TABLE
语句规定约束。
约束的分类
根据约束数据列的限制
根据约束的作用范围
位置 | 支持的约束类型 | 是否可以起约束名 | |
---|---|---|---|
列级约束 | 列的后面 | 语法都支持,但外键没有效果 | 不可以 |
表级约束 | 所有列的下面 | 默认和非空不支持,其他支持 | 可以(主键没有效果) |
根据约束起的作用
NOT NULL
:非空约束,规定某个字段不能为空UNIQUE
:唯一约束,规定某个字段在整个表中唯一的PRIMARY KEY
:主键(非空且唯一)约束FOREIGN KEY
:外键约束CHECK
:检查约束DEFAULT
:默认值约束作用
限定某个字段/某列的值不允许为空。
关键字
NOT NULL
特点
NULL
,包括INT
、FLOAT
等数据类型。NULL
,0也不等于NULL
"。添加非空约束
# 建表时
CREATE TABLE test1(
id INT NOT NULL,
last_name VARCHAR(15) NOT NULL,
email VARCHAR(25),
salary DECIMAL(10, 2)
);
#建表后
ALTER TABLE test1
MODIFY email VARCHAR(25) NOT NULL;
删除非空约束
ALTER TABLE test1
MODIFY email VARCHAR(25) NULL;
作用
用来限制某个字段/某列的值不能重复。【允许出现多个空值:NULL
】
关键字
UNIQUE
特点
MYSQL
会给唯一约束的列上默认创建一个唯一索引。添加唯一约束
键表时添加
CREATE TABLE test2(
id INT UNIQUE, # 列级约束
last_name VARCHAR(15),
email VARCHAR(25),
salary DECIMAL(10, 2),
#表级约束
CONSTRAINT uk_test2_email UNIQUE(email)
);
建表后指定唯一约束
#方式1:
ALTER TABLE test2
ADD CONSTRAINT uk_test2_sal UNIQUE(salary);
#方式2:
ALTER TABLE test2
MODIFY last_name VARCHAR(15) UNIQUE;
复合唯一约束
CREATE TABLE USER(
id INT,
`name` VARCHAR(15),
`password` VARCHAR(25),
#表级约束
CONSTRAINT uk_user_name_pwd UNIQUE(`name`, `password`)
);
删除唯一约束
ALTER TABLE test2
DROP INDEX last_name;
ALTER TABLE test2
DROP INDEX uk_test2_sal;
PRIMARY KEY
约束作用
用来唯一标识表中的一行记录。
关键字
primary key
特点
MySQL
的主键名是PRIMARY
,就算自己命名了主键约束名也没用。添加主键约束
键表时指定主键约束
# 方式一
CREATE TABLE test4(
id INT PRIMARY KEY,
last_name VARCHAR(15),
salary DECIMAL(10, 2),
email VARCHAR(25)
);
# 方式二
CREATE TABLE test5(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10, 2),
email VARCHAR(25),
CONSTRAINT pk_test5_id PRIMARY KEY(id) #没有必要起名字
);
建表后添加主键约束
CREATE TABLE test6(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10, 2),
email VARCHAR(25)
);
ALTER TABLE test6
ADD PRIMARY KEY(id);
复合主键
CREATE TABLE user1(
id INT,
NAME VARCHAR(15),
PASSWORD VARCHAR(25),
PRIMARY KEY (NAME, PASSWORD)
);
删除主键约束
ALTER TABLE test6
DROP PRIMARY KEY;
作用
某个字段的值自增。
关键字
atuo_increment
特点和要求
null
,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接赋值为具体值。指定自增约束
建表是指定自增列
CREATE TABLE test7(
id INT PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(15)
);
建表后指定自增长列
CREATE TABLE test8(
id INT PRIMARY KEY,
last_name VARCHAR(15)
);
ALTER TABLE test8
MODIFY id INT AUTO_INCREMENT;
删除自增约束
ALTER TABLE test8
MODIFY id INT;
MySQL 8.0
新特性-自增变量的持久化
在MySQL 8.0
之前,自增主键AUTO_INCREMENT
的值如果大于max(primary key) + 1
,在MySQL
重启后,会重置AUTO_INCREMENT=max(primary key) + 1
,这种现象在某些情况下会导致业务主键冲突或者其他难以发现的问题。
在MySQL 5.7
系统中,对于自增主键的分配规则,是由InnoDB
数据字典内部一个计数器来决定的,而该计数器只在内存中维护,并不会持久到磁盘中。当数据库重启时,该计数器会被初始化。
MySQL 8.0
将自增主键的计数器持久化到重做日志中。每次计数器发生改变,都会将其写入重做日志中。如果数据库重启,InnoDB
会根据重做日志中的信息来初始化计数器的内存值。
作用
限定某个表的某个字段的引用完整性。
关键字
FOREIGN KEY
主表和从表/父表和子表
特点
CREATE
)表时看指定外键约束的话,先创建主表,在创建从表。ERROR 1005(HY000): Can't create table'database.tablename'(errno: 150)
。添加外键约束
建表时添加外键约束
#①先创建主表
CREATE TABLE dept1(
dept_id INT PRIMARY KEY,
dept_name VARCHAR(15)
);
#②再创建从表
CREATE TABLE emp1(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(15),
department_id INT,
CONSTRAINT fk_emp1_dept_id FOREIGN KEY (department_id) REFERENCES dept1(dept_id)
);
建表后添加外键约束
CREATE TABLE dept2(
dept_id INT PRIMARY KEY,
dept_name VARCHAR(15)
);
CREATE TABLE emp2(
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(15),
department_id INT
);
ALTER TABLE emp2
ADD CONSTRAINT fk_emp2_dept_id FOREIGN KEY(department_id) REFERENCES dept2(dept_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, #员工所在的部门
#把修改操作设置为级联修改等级,把删除操作设置为set null等级
FOREIGN KEY (deptid) REFERENCES dept(did) ON UPDATE CASCADE ON DELETE SET NULL
);
INSERT INTO dept VALUES(1001, '教学部');
INSERT INTO dept VALUES(1002, '财务部');
INSERT INTO dept VALUES(1003, '咨询部');
INSERT INTO emp VALUES(1, '张三', 1001); #在添加这条记录时,要求部门表有1001部门
INSERT INTO emp VALUES(2, '李四', 1001);
INSERT INTO emp VALUES(3, '王五', 1002);
UPDATE dept
SET did = 1004
WHERE did = 1002;
DELETE FROM dept
WHERE did = 1004;
SELECT * FROM dept;
SELECT * FROM emp;
/* dept表
+--------+------------+
| did | dname |
+--------+------------+
| 1001 | 教学部 |
+--------+------------+
| 1003 | 咨询部 |
+--------+------------+
*/
/* emp表
+--------+------------+------------+
| eid | ename | deptid |
+--------+------------+------------+
| 1 | 张三 | 1001 |
+--------+------------+------------+
| 2 | 李四 | 1001 |
+--------+------------+------------+
| 3 | 王五 | null |
+--------+------------+------------+
*/
删除外键约束
ALTER TABLE emp1
DROP FOREIGN KEY fk_emp1_dept_id;
开发中应用场景
如果两个表之间有关系(一对一、一对多),他们之间是否一定要建外键约束?
不是。
建和不建外键约束有什么区别?
建外键约束,你的操作(创建表、删除表、添加、修改、删除)会受到限制,从语法层面受到限制。
不建外键约束,你的操作(创建表、删除表、添加、修改、删除)不受限制,要保证数据的引用完整性,只能依靠程序员的自觉,或者是在Java
程序中进行限定。
那么建和不建外建约束和查询有没有关系?
没有。
说明
在MySQL
里,外建约束是有成本的,需要消耗系统资源。对于大并发的SQL
操作,有可能会不适合。比如大型网站的中央数据库,可能会因为外建约束的系统开销而变得非常慢。所以,MySQL
允许你不使用系统自带的外键约束,在应用层面完成检查数据的一致性的逻辑。也就是说,即使你不用外键约束,也要想办法通过应用层面的附加逻辑,来实现外键约束的功能,确保数据的一致性。
阿里开发规范
【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
作用
检查某个字段的值是否符合要求,一般指的是值的范围。
关键字
CHECK
说明:MySQL 5.7
不支持
MySQL 5.7
可以使用check
约束,但check
约束对数据验证没有任何作用。添加数据时,没有任何错误或警告。但是**MySQL 8.0
中可以使用check
约束了**。
CREATE TABLE test10(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10, 2) CHECK(salary > 2000)
);
INSERT INTO test10
VALUES(1, 'Tom', 2500);
#添加失败
INSERT INTO test10
VALUES(2, 'Tom1', 1500);
SELECT *
FROM test10;
/* test10表
+--------+------------+------------+
| id | last_name | salary |
+--------+------------+------------+
| 1 | Tom | 2500.00 |
+--------+------------+------------+
*/
作用
给某个字段/某列指定默认值,一旦设置默认值,在插入数据时,如果此字段没有显示赋值,则赋值为默认值。
关键字
DEFAULT
字段加默认值
建表时添加默认值
CREATE TABLE test11(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10, 2) DEFAULT 2000
);
建表后添加默认值
CREATE TABLE test12(
id INT,
last_name VARCHAR(15),
salary DECIMAL(10, 2)
);
ALTER TABLE test12
MODIFY salary DECIMAL(8, 2) DEFAULT 2500;
删除默认值约束
ALTER TABLE test12
MODIFY salary DECIMAL(8, 2);