一、约束

1.1.什么是约束?为什么要有约束?

  • 为了保证数据的一致性和完整性,SQL规范以约束的方式对表数据进行额外的条件限制。
  • 约束是表级别的强制规定
  • 可以在创建表时规定约束(通过 CREATE TABLE 语句),或者在表创建之后也可以(通 过 ALTER TABLE 语句)

1.2.MySQL有哪些约束?

有以下六种约束:

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

注意:MySQL不支持check约束,但可以使用check约束,而没有任何效果; 具体细节可以参阅W3Cschool手册

MySQL 8.0.16 是第一个支持 CHECK 约束的版本。如果使用 MySQL 8.0.15 或更早版本,MySQL 参考手册中明确表示:CHECK 子句会被解析,但被所有存储引擎忽略。如果您想要使用 CHECK 约束,可以尝试使用触发器来实现类似的功能。

1.3.约束分类

根据约束数据列的限制,约束可分为:

  • 单列约束:每个约束只约束一列
  • 多列约束:每个约束可约束多列数据

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

  • 列级约束只能作用在一个列上,跟在列的定义后面
  • 表级约束可以作用在多个列上,不与列一起,而是 单独定义

二、NOT NULL 约束

2.1.NOT NULL 约束说明

非空约束用于确保当前列的值不为空值,非空约束只能出现在表对象的列上。

Null类型特征:

  • 所有的类型的值都可以是null,包括int、 float等数据类型
  • 空字符串””不等于null,0也不等于null

2.2.NOT NULL 约束

创建表时使用not null 约束:

CREATE TABLE emp(
    id INT(10) NOT NULL,
    name VARCHAR(20) NOT NULL DEFAULT 'abc',
    sex CHAR NULL
)

增加not null 约束:

ALTER TABLE emp MODIFY sex VARCHAR(30) NOT NULL;

查看表结构:

MySQL约束和三大范式_主键

上面设置为空约束后,插入数据的时候必须要有值才可以,否则会报错如下:

MySQL约束和三大范式_MySQL_02

2.3.取消NOT NULL 约束

  • 取消 not null 约束:
ALTER TABLE emp MODIFY sex VARCHAR(30) NULL;

查看结构:

MySQL约束和三大范式_主键_03

  • 取消 not null 约束,增加默认值
ALTER TABLE emp MODIFY name VARCHAR(15) DEFAULT 'augus' NULL;

注意:设置默认值后如果插入数据不设置name字段的值,则会使用默认值

二、UNIQUE约束

2.1.UNIQUE约束

同一个表可以有多个唯一约束,多个列组合的约束。 在创建唯一约束的时候,如果不给唯一约束名称,就默认和列名相同。MySQL会给唯一约束的列上默认创建一个唯一索引

# 给name列添加唯一约束
CREATE TABLE user(
id INT NOT NULL,
# 给name添加唯一约束
name VARCHAR(25) UNIQUE,
password VARCHAR(16)
)
# 给name和password两列绑定组合唯一约束
CREATE TABLE user(
id INT NOT NULL,
name VARCHAR(25),
password VARCHAR(16),
# 使用表级约束语法,name和password列组合设置唯一约束
CONSTRAINT uk_name_pwd UNIQUE(name,password)
)

上面案例表示用户名和密码组合不能重复

2.2.添加唯一约束

我们除了在创建表的时候添加唯一性约束之外,也可以在创建表完成后,通过alter添加唯一约束:

# 添加唯一约束
ALTER TABLE user ADD UNIQUE(name,password)
# 添加唯一约束,指定约束名称
ALTER TABLE user ADD CONSTRAINT uk_name_pwd UNIQUE(name,password)
# 给单列添加唯一约束
ALTER TABLE user MODIFY name VARCHAR(20) UNIQUE;

2.3.删除约束

ALTER TABLE user DROP INDEX uk_name_pwd

三、PRIMARY KEY 约束

3.1.PRIMARY KEY 约束说明

  • 主键约束相当于唯一约束+非空约束的组合,主键约束列不允许重复,也不允许出现空值
  • 如果是多列组合的主键约束,那么这些列都不允 许为空值,并且组合的值不允许重复。
  • 每个表最多只允许一个主键,建立主键约束可以 在列级别创建,也可以在表级别上创建。
  • MySQL的主键名总是PRIMARY,当创建主键约束 时,系统默认会在所在的列和列组合上建立对应的 唯一索引。

3.2.添加PRIMARY KEY 约束

  • 列级模式
CREATE TABLE emp1(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20)
)
  • 表级模式
CREATE TABLE emp2(
id INT AUTO_INCREMENT,
name VARCHAR(20),
CONSTRAINT emp2_id_pk PRIMARY KEY(id)
)
  • 组合模式
CREATE TABLE emp3(
id INT NOT NULL,
name VARCHAR(20),
pwd  VARCHAR(20),
CONSTRAINT emp3_id_pk PRIMARY KEY(name,pwd)
)

3.3.主键相关操作

删除主键约束

ALTER TABLE emp3 DROP PRIMARY KEY

添加主键约束

ALTER TABLE emp3 ADD PRIMARY KEY(id,name)

修改主键约束

ALTER TABLE emp3 MODIFY id INT PRIMARY KEY;

四、FOREIGN KEY 约束

4.1.FOREIGN KEY 约束

  • 外键约束是保证一个或两个表之间的参照完整性, 外键是构建于一个表的两个字段或是两个表的两个字 段之间的参照关系。
  • 从表的外键值必须在主表中能找到或者为空。当主 表的记录被从表参照时,主表的记录将不允许删除, 如果要删除数据,需要先删除从表中依赖该记录的数 据,然后才可以删除主表的数据。
  • 还有一种就是级联删除子表数据。

注意:

  • 外键约束的参照列,在主表中引用的只能是 主键或唯一键约束的列
  • 同一个表可以有多个外键约束

4.2.添加FOREIGN KEY 约束

创建外键约束:

主表:

CREATE TABLE dept(
dept_id INT AUTO_INCREMENT PRIMARY KEY,
dept_name VARCHAR(20)
);

从表

CREATE TABLE emp(
emp_id INT AUTO_INCREMENT PRIMARY KEY,
last_name VARCHAR(15),
dept_id INT,
CONSTRAINT emp_dept_id_fk FOREIGN KEY(dept_id) REFERENCES dept(dept_id)
);

4.3.创建多列外键组合

创建多列外键组合,必须使用表级约束

主表:

CREATE TABLE classes(
id INT,
name VARCHAR(20),
number INT,
# 创建主键
PRIMARY KEY(name,number)
);

从表:

CREATE TABLE student(
id INT AUTO_INCREMENT PRIMARY KEY,
class_name VARCHAR(20),
class_number INT,
FOREIGN KEY(class_name,class_number) REFERENCES classes(name,number)
)

4.4.删除和添加外键约束

  • 删除外键约束:
ALTER TABLE emp DROP FOREIGN KEY emp_dept_id_fk;
  • 增加外键约束
ALTER TABLE emp 
ADD CONSTRAINT emp_dept_id_fk FOREIGN KEY(dept_id)
REFERENCES dept(dept_id);

说明:

  • FOREIGN KEY: 在表级指定子表中的列
  • REFERENCES: 标示在父表中的列
  • ON DELETE CASCADE(级联删除): 当父表中的列被删除 时,子表中相对应的列也被删除
  • ON DELETE SET NULL(级联置空): 子表中相应的列置空
CREATE TABLE student(
id INT AUTO_INCREMENT PRIMARY KEY,
class_name VARCHAR(20),
class_number INT,
# 表级别联合外键
FOREIGN KEY(class_name,class_number) REFERENCES classes(name,number) ON DELETE CASCADE
)

五、CHECK约束

MySQL可以使用check约束,但check约束对数据 验证没有任何作用,添加数据时,没有任何错误或 警告

CREATE TABLE emp4(
id INT AUTO_INCREMENT,
name VARCHAR(20),
age INT CHECK(age>20),
PRIMARY KEY(id)
);

INSERT INTO emp4 VALUES(1,'top',14)
INSERT INTO emp4 VALUES(2,'tom',15)
INSERT INTO emp4 VALUES(3,'augus',23)

六、DEFAULT默认值约束

在插入数据时,如果不写入就使用默认值,如果写入就使用新的值

# 创建表
DROP TABLE IF EXISTS dept2
CREATE TABLE dept2(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(25),
location VARCHAR(50) DEFAULT 'shanghai');


INSERT INTO dept2 VALUES (1,'产品部','beijing')
# location字段不插入则会使用默认值
INSERT INTO dept2 (id,name) VALUES (2,'项目部')
INSERT INTO dept2 (name,location) VALUES ('生产部','深圳')

SELECT * FROM dept2

DESC dept2

七、MySQL三大范式

目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。而通常我们用的最多的就是第一范式(1NF)、第二范式(2NF)、第三范式(3NF),也就是本文要讲的“三大范式”。

7.1.第一范式(1NF)

第一范式(1NF):要求数据库表的每一列都是不可分割的原子数据项。举例说明:

MySQL约束和三大范式_MySQL_04

在上面的表中,“家庭信息”和“学校信息”列均不满足原子性的要求,故不满足第一范式,调整如下:

MySQL约束和三大范式_第二范式_05

可见,调整后的每一列都是不可再分的,因此满足第一范式(1NF)

7.2.第二范式(2NF)

  • 第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)
  • 第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。

举例说明:

MySQL约束和三大范式_MySQL_06

在上图所示的情况中,同一个订单中可能包含不同的产品,因此主键必须是“订单号”和“产品号”联合组成,

但可以发现,产品数量、产品折扣、产品价格与“订单号”和“产品号”都相关,但是订单金额和订单时间仅与“订单号”相关,与“产品号”无关,这样就不满足第二范式的要求,调整如下,需分成两个表:

MySQL约束和三大范式_MySQL_07

7.3.第三范式(3NF)

  • 第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)
  • 第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

举例说明:

MySQL约束和三大范式_主键_08

上表中,所有属性都完全依赖于学号,所以满足第二范式,但是“班主任性别”和“班主任年龄”直接依赖的是“班主任姓名”,而不是主键“学号”,所以需做如下调整:

MySQL约束和三大范式_MySQL_09

这样以来,就满足了第三范式的要求。

注意:如果把上表中的班主任姓名改成班主任教工号可能更确切,更符合实际情况,不过只要能理解就行。