• 【MySQL】-【约束】


    约束

    约束概述

    一、为什么需要约束:数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。为了保证数据的完整性,SQL规范以约束的方式对表数据进行额外的条件限制。从以下四个方面考虑:

    1. 实体完整性(Entity Integrity):例如同一个表中,不能存在两条完全相同无法区分的记录
    2. 域完整性(Domain Integrity):例如:年龄范围0-120,性别范围“男/女”
    3. 引用完整性(Referential Integrity):例如员工所在部门,在部门表中要能找到这个部门
    4. 用户自定义完整性(User-defined Integrity):例如用户名唯一、密码不能为空等,本部门经理的工资不得高于本部门职工的平均工资的5倍。

    二、约束是对表中字段的限制。
    三、可以在创建表时规定约束(通过CREATE TABLE语句),或者在表创建之后通过ALTER TABLE语句规定约束。
    四、约束的分类

    1. 根据约束数据列的限制,约束可分为:
      单列约束:每个约束只约束一列
      多列约束:每个约束可约束多列数据
    2. 根据约束的作用范围,约束可分为:
      列级约束:只能作用在一个列上,跟在列的定义后面
      表级约束:可以作用在多个列上,不与列一起,而是单独定义
      在这里插入图片描述
    3. 根据约束起的作用,约束可分为:
      NOT NULL 非空约束,规定某个字段不能为空
      UNIQUE 唯一约束,规定某个字段在整个表中是唯一的
      PRIMARY KEY 主键(非空且唯一)约束
      FOREIGN KEY 外键约束
      CHECK 检查约束。MySQL不支持check约束,但可以使用check约束,而没有任何效果
      DEFAULT 默认值约束

    五、查看某个表已有的约束

    #information_schema数据库名(系统库)
    #table_constraints表名称(专门存储各个表的约束)
    SELECT * FROM information_schema.table_constraints
    WHERE table_name = '表名称';
    
    • 1
    • 2
    • 3
    • 4

    非空约束NOT NULL

    一、作用:限定某个字段/某列的值不允许为空
    二、特点:

    1. 默认,所有的类型的值都可以是NULL,包括INT、FLOAT等数据类型
    2. 非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空
    3. 一个表可以有很多列都分别限定了非空
    4. 空字符串''不等于NULL,0也不等于NULL

    三、添加非空约束

    1. 在CREATE TABLE时添加约束,语法:
    CREATE TABLE 表名称(
    	字段名 数据类型,
    	字段名 数据类型 NOT NULL,
    	字段名 数据类型 NOT NULL
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5

    案例:

    CREATE DATABASE dbtest13;
    USE dbtest13;
    CREATE TABLE test1(
    	id INT NOT NULL,
    	last_name VARCHAR(15) NOT NULL,
    	email VARCHAR(25),
    	salary DECIMAL(10,2)
    );
    
    DESC test1;
    
    INSERT INTO test1(id,last_name,email,salary)
    VALUES(1,'Tom','tom@126.com',3400);
    
    #错误:Column 'last_name' cannot be null
    INSERT INTO test1(id,last_name,email,salary)
    VALUES(2,NULL,'tom1@126.com',3400);
    
    #错误:Column 'id' cannot be null
    INSERT INTO test1(id,last_name,email,salary)
    VALUES(NULL,'Jerry','jerry@126.com',3400);
    
    # 错误:Field 'last_name' doesn't have a default value
    # 当我们没有给某字段赋值时,他先找有没有默认值,没有默认值会给他赋值为null,
    # 但是前面我们约束了他不能为null,所以会报错没有默认值
    INSERT INTO test1(id,email)
    VALUES(2,'abc@126.com');
    
    # 修改时也不能改为null,报错:Column 'last_name' cannot be null
    UPDATE test1
    SET last_name = NULL
    WHERE id = 1;
    
    UPDATE test1
    SET email = 'tom@126.com'
    WHERE id = 1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    1. 在ALTER TABLE时添加约束,语法:alter table 表名称 modify 字段名 数据类型 not null;,案例:ALTER TABLE test1 MODIFY email VARCHAR(25) NOT NULL;

    四、删除非空约束:去掉not null,相当于修改某个非注解字段,该字段允许为空。

    1. 语法:
      (1)alter table 表名称 modify 字段名 数据类型 NULL;
      (2)alter table 表名称 modify 字段名 数据类型;
    2. 案例:
    ALTER TABLE test1
    MODIFY email VARCHAR(25) NULL;
    
    • 1
    • 2

    唯一性约束UNIQUE

    一、作用:用来限制某个字段/某列的值不能重复。
    二、特点:

    1. 同一个表可以有多个唯一约束。
    2. 唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一。
    3. 唯一性约束允许列值为空。
    4. 在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同。
    5. MySQL会给唯一约束的列上默认创建一个唯一索引。

    三、添加唯一约束

    1. 在CREATE TABLE时添加约束,语法:
    create table 表名称(
    	字段名 数据类型,
    	字段名 数据类型 unique,
    	字段名 数据类型 unique key,
    	字段名 数据类型
    );
    # 或
    create table 表名称(
    	字段名 数据类型,
    	字段名 数据类型,
    	字段名 数据类型,
    	[constraint 约束名] unique key(字段名)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    案例:

    CREATE TABLE test2(
    id INT UNIQUE, #列级约束
    last_name VARCHAR(15) ,
    email VARCHAR(25),
    salary DECIMAL(10,2),
    #表级约束
    CONSTRAINT uk_test2_email UNIQUE(email)
    # CONSTRAINT uk_test2_email是对这个约束命名,
    # 表级约束也可以写作:UNIQUE(email)
    # 这样在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同。
    # 在删除约束时会用到约束名
    );
    
    INSERT INTO test2(id,last_name,email,salary)
    VALUES(1,'Tom','tom@126.com',4500);
    
    #错误:Duplicate entry '1' for key 'test2.id'
    INSERT INTO test2(id,last_name,email,salary)
    VALUES(1,'Tom1','tom1@126.com',4600);
    
    #错误:Duplicate entry 'tom@126.com' for key 'test2.uk_test2_email'
    INSERT INTO test2(id,last_name,email,salary)
    VALUES(2,'Tom1','tom@126.com',4600);
    
    #可以向声明为unique的字段上添加null值。而且可以多次添加null
    INSERT INTO test2(id,last_name,email,salary)
    VALUES(2,'Tom1',NULL,4600);
    
    INSERT INTO test2(id,last_name,email,salary)
    VALUES(3,'Tom2',NULL,4600);
    
    SELECT * FROM test2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    1. 在ALTER TABLE时添加约束,语法:
    alter table 表名称 add unique key(字段列表);
    # 或
    alter table 表名称 modify 字段名 字段类型 unique;
    
    • 1
    • 2
    • 3

    案例:

    DESC test2;
    
    UPDATE test2
    SET salary = 5000
    WHERE id = 3;
    #方式1:类似于表约束
    ALTER TABLE test2
    ADD CONSTRAINT uk_test2_sal UNIQUE(salary);
    #方式2:类似于列约束
    ALTER TABLE test2
    MODIFY last_name VARCHAR(15) UNIQUE;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    四、复合唯一约束,对多个字段的组合进行约束,例如学号为1的同学选了数学课,那么接下来他就不能再选数学课了,即(1,数学课)和(1,数学课)一致。语法:

    create table 表名称(
    字段名 数据类型,
    字段名 数据类型,
    字段名 数据类型,
    unique key(字段列表) #字段列表中写的是多个字段名,多个字段名用逗号分隔,表示那么是复合唯一,即多个字段的组合是唯一的
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    案例:

    # 案例1:
    CREATE TABLE USER(
    id INT,
    `name` VARCHAR(15),
    `password` VARCHAR(25),
    
    #表级约束
    CONSTRAINT uk_user_name_pwd UNIQUE(`name`,`password`)
    );
    
    INSERT INTO USER
    VALUES(1,'Tom','abc');
    #可以成功的:
    INSERT INTO USER
    VALUES(1,'Tom1','abc');
    
    # 案例2:
    #学生表
    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)  #复合唯一
    );
    INSERT INTO student VALUES(1,'张三','13710011002','101223199012015623');#成功
    INSERT INTO student VALUES(2,'李四','13710011003','101223199012015624');#成功
    INSERT INTO course VALUES(1001,'Java'),(1002,'MySQL');#成功
    
    SELECT * FROM student;
    
    SELECT * FROM course;
    
    INSERT INTO student_course VALUES
    (1, 1, 1001, 89),
    (2, 1, 1002, 90),
    (3, 2, 1001, 88),
    (4, 2, 1002, 56);#成功
    
    SELECT * FROM student_course;
    
    #错误:Duplicate entry '2-1002' for key 'student_course.sid'
    INSERT INTO student_course VALUES
    (5,2,1002,67);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    五、删除唯一约束

    1. 添加唯一性约束的列上也会自动创建唯一索引。
    2. 删除唯一约束只能通过删除唯一索引的方式删除。
    3. 删除时需要指定唯一索引名,唯一索引名就和唯一约束名一样。
    4. 如果创建唯一约束时未指定名称,如果是单列,就默认和列名相同;如果是组合列,那么默认和()中排在第一个的列名相同。也可以自定义唯一性约束名。
    ALTER TABLE test2
    DROP INDEX last_name;
    
    ALTER TABLE test2
    DROP INDEX uk_test2_sal;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以通过show index from 表名称;查看表的索引

    主键PRIMARY KEY约束

    一、作用:用来唯一标识表中的一行记录。
    二、特点:

    1. 主键约束相当于唯一约束+非空约束的组合,主键约束列不允许重复,也不允许出现空值。
    2. 一个表最多只能有一个主键约束,建立主键约束可以在列级别创建,也可以在表级别上创建。
    3. 主键约束对应着表中的一列或者多列(复合主键)
    4. 如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复。
    5. MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用。
    6. 当创建主键约束时,系统默认会在所在的列或列组合上建立对应的主键索引(能够根据主键查询的,就根据主键查询,效率更高)。如果删除主键约束了,主键约束对应的索引就自动删除了。
    7. 需要注意的一点是,不要修改主键字段的值。因为主键是数据记录的唯一标识,如果修改了主键的值,就有可能会破坏数据的完整性。

    三、添加主键约束

    1. 建表时指定主键约束,语法:
    create table 表名称(
    字段名 数据类型 primary key, #列级模式
    字段名 数据类型,
    字段名 数据类型
    );
    create table 表名称(
    字段名 数据类型,
    字段名 数据类型,
    字段名 数据类型,
    [constraint 约束名] primary key(字段名) #表级模式
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    案例:

    #一个表中最多只能有一个主键约束。
    #错误:Multiple primary key defined
    CREATE TABLE test3(
    id INT PRIMARY KEY, #列级约束
    last_name VARCHAR(15) PRIMARY KEY,
    salary DECIMAL(10,2),
    email VARCHAR(25)
    );
    
    # 主键约束特征:非空且唯一,用于唯一的标识表中的一条记录。
    CREATE TABLE test4(
    id INT PRIMARY KEY, #列级约束
    last_name VARCHAR(15),
    salary DECIMAL(10,2),
    email VARCHAR(25)
    );
    
    #MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用。
    CREATE TABLE test5(
    id INT , 
    last_name VARCHAR(15),
    salary DECIMAL(10,2),
    email VARCHAR(25),
    #表级约束
    CONSTRAINT pk_test5_id PRIMARY KEY(id)  #没有必要起名字。
    );
    
    SELECT * FROM information_schema.table_constraints 
    WHERE TABLE_NAME = 'test5';
    
    INSERT INTO test4(id,last_name,salary,email)
    VALUES(1,'Tom',4500,'tom@126.com');
    
    #错误:Duplicate entry '1' for key 'test4.PRIMARY'
    INSERT INTO test4(id,last_name,salary,email)
    VALUES(1,'Tom',4500,'tom@126.com');
    
    #错误:Column 'id' cannot be null
    INSERT INTO test4(id,last_name,salary,email)
    VALUES(NULL,'Tom',4500,'tom@126.com');
    
    SELECT * FROM test4;
    
    
    CREATE TABLE user1(
    id INT,
    NAME VARCHAR(15),
    PASSWORD VARCHAR(25),
    PRIMARY KEY (NAME,PASSWORD)
    );
    #如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复。
    INSERT INTO user1
    VALUES(1,'Tom','abc');
    INSERT INTO user1
    VALUES(1,'Tom1','abc');# 这一条语句和上面那条都会执行成功
    #错误:Column 'name' cannot be null
    INSERT INTO user1
    VALUES(1,NULL,'abc');# 指定了PRIMARY KEY的列都不能为null
    
    SELECT * FROM user1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    1. 建表后增加主键约束,语法:
    ALTER TABLE 表名称 ADD PRIMARY KEY(字段列表); #字段列表可以是一个字段,也可以是多个字段,如果是多个字段的话,是复合主键
    
    • 1

    案例:

    CREATE TABLE test6(
    id INT ,
    last_name VARCHAR(15),
    salary DECIMAL(10,2),
    email VARCHAR(25)
    );
    
    DESC test6;
    
    ALTER TABLE test6
    ADD PRIMARY KEY (id);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    四、删除主键约束,语法:alter table 表名称 drop primary key;,案例:ALTER TABLE test6 DROP PRIMARY KEY;

    注意:

    1. 在实际开发中,不会去删除表中的主键约束!主键约束声明在一个字段上了,那么该字段上会自动添加一个主键索引,整个表的数据是依据主键索引构建的,对应的数据结构是B+树,如果主键约束被删了,那B+树也就没了
    2. 删除主键约束,不需要指定主键名,因为一个表只有一个主键,删除主键约束后,非空还存在,只是不约束他【非空且唯一】了,他还是不能为空,但是他可以不唯一

    自增列:AUTO_INCREMENT

    一、作用:某个字段的值自增
    二、特点和要求:

    1. 一个表最多只能有一个自增长列
    2. 当需要产生唯一标识符或顺序值时,可设置自增长
    3. 自增长列约束的列必须是键列(主键列,唯一键列)
    4. 自增约束的列的数据类型必须是整数类型
    5. 如果自增列指定了0和null,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接赋值为具体值。
      三、如何指定自增约束
    6. 建表时:
    CREATE TABLE test7(
    id INT PRIMARY KEY AUTO_INCREMENT,
    last_name VARCHAR(15) 
    );
    #开发中,一旦主键作用的字段上声明有AUTO_INCREMENT,则我们在添加数据时,就不要给主键
    #对应的字段去赋值了。
    INSERT INTO test7(last_name)
    VALUES('Tom');# 不给主键赋值,他会自增
    
    SELECT * FROM test7;
    
    #当我们向主键(含AUTO_INCREMENT)的字段上添加0 或 null时,实际上会自动的往上添加指定的字段的数值
    INSERT INTO test7(id,last_name)
    VALUES(0,'Tom');
    
    INSERT INTO test7(id,last_name)
    VALUES(NULL,'Tom');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    最后两行代码能执行成功,多执行几次,结果如下:
    在这里插入图片描述
    如果指定了id值,且该值不存在,也会添加成功:

    INSERT INTO test7(id,last_name)
    VALUES(10,'Tom');
    
    • 1
    • 2

    在这里插入图片描述

    INSERT INTO test7(id,last_name)
    VALUES(-10,'Tom');
    
    • 1
    • 2

    在这里插入图片描述
    以后再执行INSERT INTO test7(last_name) VALUES('Tom');,id会从11开始增加

    1. 建表后添加,语法:alter table 表名称 modify 字段名 数据类型 auto_increment;,案例:
    CREATE TABLE test8(
    id INT PRIMARY KEY ,
    last_name VARCHAR(15) 
    );
    
    DESC test8;
    
    ALTER TABLE test8
    MODIFY id INT AUTO_INCREMENT;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    四、删除自增约束的语法:alter table 表名称 modify 字段名 数据类型; #去掉auto_increment相当于删除,对比发现,建表后添加的语法是:alter table 表名称 modify 字段名 数据类型 auto_increment;#给这个字段增加自增约束
    五、MySQL 8.0新特性—自增变量的持久化
    在MySQL 8.0之前,自增主键AUTO_INCREMENT的值如果大于max(primary key)+1,在MySQL重启后,会重置AUTO_INCREMENT=max(primary key)+1,这种现象在某些情况下会导致业务主键冲突或者其他难以发现的问题。

    1. 在MySQL 5.7中演示:
    CREATE TABLE test9(
    id INT PRIMARY KEY AUTO_INCREMENT
    );
    
    INSERT INTO test9
    VALUES(0),(0),(0),(0);# 现在表中的数据id从1到4
    
    SELECT * FROM test9;
    
    DELETE FROM test9
    WHERE id = 4;# 删除id为4的数据
    
    INSERT INTO test9
    VALUES(0);# 新增的这条数据id为5,而不是4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    DELETE FROM test9
    WHERE id = 5;# 删除id=5的数据后,数据库只剩id为1、2、3的数据
    
    #重启服务器
    SELECT * FROM test9;
    
    INSERT INTO test9
    VALUES(0);# 新增的这条数据id=为4
    
    SELECT * FROM test9;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    从结果可以看出,新插入的0值分配的是4,按照重启前的操作逻辑,此处应该分配6。出现上述结果的主要原因是自增主键没有持久化。 在MySQL 5.7系统中,对于自增主键的分配规则,是由InnoDB数据字典内部一个 计数器来决定的,而该计数器只在 内存中维护 ,并不会持久化到磁盘中。当数据库重启时,该计数器会被初始化。

    1. 以上代码再在MySQL 8.0中演示
    CREATE TABLE test9(
    id INT PRIMARY KEY AUTO_INCREMENT
    );
    
    INSERT INTO test9
    VALUES(0),(0),(0),(0);# 现在表中的数据id从1到4
    
    SELECT * FROM test9;
    
    DELETE FROM test9
    WHERE id = 4;# 删除id为4的数据
    
    INSERT INTO test9
    VALUES(0);# 新增的这条数据id为5,而不是4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    DELETE FROM test9
    WHERE id = 5;# 删除id=5的数据后,数据库只剩id为1、2、3的数据
    
    #重启服务器
    SELECT * FROM test9;
    
    INSERT INTO test9
    VALUES(0);# 新增的这条数据id=为6
    
    SELECT * FROM test9;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    从结果可以看出,自增变量已经持久化了。MySQL 8.0将自增主键的计数器持久化到重做日志中。每次计数器发生改变,都会将其写入重做日志中。如果数据库重启,InnoDB会根据重做日志中的信息来初始化计数器的内存值。

    FOREIGN KEY 约束

    一、作用:限定某个表的某个字段的引用完整性,即从表中添加的字段的值必须在主表中存在
    在这里插入图片描述
    有一张部门表,一张员工表,员工表的DEPARTMENT_ID与部门表的DEPARTMENT_ID相关联,现在向员工表中添加数据,必须要员工表的DEPARTMENT_ID的值在部门表的DEPARTMENT_ID中存在,才能添加成功。部门表的DEPARTMENT_ID是主键,员工表的DEPARTMENT_ID是外键,且部门表被称为主表、父表,员工表被称为从表、子表
    二、特点

    1. ==从表的外键列,必须引用/参考主表的主键或唯一约束的列。==用上面的例子来说明,部门表的DEPARTMENT_ID必须有主键约束或唯一约束
      为什么?因为被依赖/被参考的值必须是唯一的。
    2. 在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名(例如student_ibfk_1;),也可以指定外键约束名。
    3. 创建(CREATE)表时就指定外键约束的话,先创建主表,再创建从表
    4. 删表时,先删从表(或先删除外键约束),再删除主表
    5. 当主表的记录被从表参照时,主表的记录将不允许删除,如果要删除数据,需要先删除从表中依赖该记录的数据,然后才可以删除主表的数据
    6. 在“从表”中指定外键约束,并且一个表可以建立多个外键约束
    7. 从表的外键列与主表被参照的列名字可以不相同,但是数据类型必须一样,逻辑意义一致。如果类型不一样,创建子表时,就会出现错误ERROR 1005 (HY000): Can't create table'database.tablename'(errno: 150)。例如:都是表示部门编号,都是int类型。
    8. ==当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。==但是索引名是外键的约束名。(根据外键查询效率很高)
    9. 删除外键约束后,必须手动删除对应的索引

    三、添加外键约束

    1. 在CREATE TABLE时添加,语法:
    create table 主表名称(
    字段1 数据类型 primary key,
    字段2 数据类型
    );
    create table 从表名称(
    字段1 数据类型 primary key,
    字段2 数据类型,
    [CONSTRAINT <外键约束名称>] FOREIGN KEY(从表的某个字段) references 主表名(被参考字段)
    );
    #(从表的某个字段)的数据类型必须与主表名(被参考字段)的数据类型一致,逻辑意义也一样
    #(从表的某个字段)的字段名可以与主表名(被参考字段)的字段名一样,也可以不一样
    -- FOREIGN KEY: 在表级指定子表中的列
    -- REFERENCES: 标示在父表中的列
    
    #或
    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) #在从表中指定外键约束
    #emp表的deptid和和dept表的did的数据类型一致,意义都是表示部门的编号
    );
    #说明:
    #(1)主表dept必须先创建成功,然后才能创建emp表,指定外键成功。
    #(2)删除表时,先删除从表emp,再删除主表dept
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    案例:

    #①先创建主表
    CREATE TABLE dept1(
    dept_id INT,
    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)
    );
    
    #上述操作报错,因为主表中的dept_id上没有主键约束或唯一性约束。
    #③ 为主表添加主键约束
    ALTER TABLE dept1 ADD PRIMARY KEY (dept_id);
    
    DESC dept1;
    
    #④ 再创建从表
    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)
    );
    
    DESC emp1;
    
    SELECT * FROM information_schema.table_constraints 
    WHERE TABLE_NAME = 'emp1';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述
    以下操作会失败:

    #添加失败
    INSERT INTO emp1
    VALUES(1001,'Tom',10);
    
    # 这个操作才会成功,先让关联字段在主表中有数据,再在从表中添加数据
    INSERT INTO dept1
    VALUES(10,'IT');
    #在主表dept1中添加了10号部门以后,我们就可以在从表中添加10号部门的员工
    INSERT INTO emp1
    VALUES(1001,'Tom',10);
    
    # 因为该字段被关联,所以删除喝更新都会失败
    #删除失败
    DELETE FROM dept1
    WHERE dept_id = 10;
    
    #更新失败
    UPDATE dept1
    SET dept_id = 20
    WHERE dept_id = 10;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 建表后添加约束,语法:ALTER TABLE 从表名 ADD [CONSTRAINT 约束名] FOREIGN KEY (从表的字段) REFERENCES 主表名(被引用字段) [on update xx][on delete xx];。案例:
    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);
    
    SELECT * FROM information_schema.table_constraints 
    WHERE TABLE_NAME = 'emp2';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述
    四、约束等级

    1. Cascade方式:在父表上update/delete记录时,同步update/delete掉子表的匹配记录
    2. Set null方式:在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子表的外键列不能为not null
    3. No action方式:如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
    4. Restrict方式:同no action, 都是立即检查外键约束。如果没有指定等级,则默认使用该等级
    5. Set default方式(在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置成一个默认的值,但Innodb不能识别
    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等级
    );
    
    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;
    
    SELECT * FROM dept;
    SELECT * FROM emp;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在这里插入图片描述
    在这里插入图片描述

    DELETE FROM dept
    WHERE did = 1004;
    
    SELECT * FROM dept;
    SELECT * FROM emp;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    在这里插入图片描述
    对于外键约束,最好是采用:ON UPDATE CASCADE ON DELETE RESTRICT的方式。
    五、删除外键约束的流程:

    1. 第一步先查看约束名和删除外键约束
    SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';#查看某个表的约束名
    ALTER TABLE 从表名 DROP FOREIGN KEY 外键约束名;
    
    • 1
    • 2
    1. 第二步查看索引名和删除索引。(注意,只能手动删除)
    SHOW INDEX FROM 表名称; #查看某个表的索引名
    ALTER TABLE 从表名 DROP INDEX 索引名;
    
    • 1
    • 2

    案例:

    #一个表中可以声明有多个外键约束
    USE atguigudb;
    SELECT * FROM information_schema.table_constraints 
    WHERE TABLE_NAME = 'employees';
    
    USE dbtest13;
    
    SELECT * FROM information_schema.table_constraints 
    WHERE TABLE_NAME = 'emp1';
    
    #删除外键约束
    
    ALTER TABLE emp1
    DROP FOREIGN KEY fk_emp1_dept_id;
    
    #再手动的删除外键约束对应的普通索引
    SHOW INDEX FROM emp1;
    
    ALTER TABLE emp1
    DROP INDEX fk_emp1_dept_id;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    六、开发场景:前面讲的非空约束、主键约束(一定要有,影响索引、B+树)、唯一约束在开发中一般都要用。

    1. 问题1:如果两个表之间有关系(一对一、一对多),比如:员工表和部门表(一对多),它们之间是否一定要建外键约束?
      答:不是的
    2. 问题2:建和不建外键约束有什么区别?
      答:建外键约束,你的操作(创建表、删除表、添加、修改、删除)会受到限制,从语法层面受到限制。例如:在员工表中不可能添加一个员工信息,它的部门的值在部门表中找不到。不建外键约束,你的操作(创建表、删除表、添加、修改、删除)不受限制,要保证数据的 引用完整性 ,只能依 靠程序员的自觉 ,或者是 在Java程序中进行限定 。例如:在员工表中,可以添加一个员工的信息,它的部门指定为一个完全不存在的部门。
    3. 问题3:那么建和不建外键约束和查询有没有关系?
      答:没有

    总结:在MySQL里,外键约束是有成本的,需要消耗系统资源。对于大并发的SQL操作,有可能会不适合。比如大型网站的中央数据库,可能会因为外键约束的系统开销而变得非常慢 。所以,MySQL允许你不使用系统自带的外键约束,在应用层面完成检查数据一致性的逻辑。也就是说,即使你不用外键约束,也要想办法通过应用层面的附加逻辑,来实现外键约束的功能,确保数据的一致性。

    阿里开发规范
    【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
    说明:(概念解释)学生表中的student_id是主键,那么成绩表中的student_id则为外键。如果更新学生表中的student_id,同时触发成绩表中的student_id更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式 、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度 。

    CHECK 约束

    一、作用:检查某个字段的值是否符号xx要求,一般指的是值的范围
    二、MySQL 5.7不支持check约束:MySQL5.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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    DEFAULT约束

    一、作用:给某个字段/某列指定默认值,一旦设置默认值,在插入数据时,如果此字段没有显式赋值,则赋值为默认值。
    二、如何给字段加默认值

    1. 建表时添加,语法:
    create table 表名称(
    字段名 数据类型 primary key,
    字段名 数据类型 unique key not null,
    字段名 数据类型 unique key,
    字段名 数据类型 not null default 默认值,
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意:默认值约束一般不在唯一键和主键列上加

    CREATE TABLE test11(
    id INT,
    last_name VARCHAR(15),
    salary DECIMAL(10,2) DEFAULT 2000
    );
    
    DESC test11;
    
    INSERT INTO test11(id,last_name,salary)
    VALUES(1,'Tom',3000);
    
    INSERT INTO test11(id,last_name)
    VALUES(2,'Tom1');
    
    SELECT * 
    FROM test11;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    1. 建表后添加,语法:
    alter table 表名称 modify 字段名 数据类型 default 默认值;
    #如果这个字段原来有非空约束,你还保留非空约束,那么在加默认值约束时,还得保留非空约束,否则非空约束就被删除了
    #同理,在给某个字段加非空约束也一样,如果这个字段原来有默认值约束,你想保留,也要在modify语句中保留默认值约束,否则就删除了
    alter table 表名称 modify 字段名 数据类型 default 默认值 not null;
    
    • 1
    • 2
    • 3
    • 4

    案例:

    CREATE TABLE test12(
    id INT,
    last_name VARCHAR(15),
    salary DECIMAL(10,2)
    );
    
    DESC test12;
    
    ALTER TABLE test12
    MODIFY salary DECIMAL(8,2) DEFAULT 2500;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、删除默认值约束,语法:

    alter table 表名称 modify 字段名 数据类型;#删除默认值约束,也不保留非空约束
    alter table 表名称 modify 字段名 数据类型 not null; #删除默认值约束,保留非空约束
    alter table employee modify gender char; #删除gender字段默认值约束,如果有非空约束,也一并删除
    alter table employee modify tel char(11) not null;#删除tel字段默认值约束,保留非空约束
    
    • 1
    • 2
    • 3
    • 4

    案例:

    ALTER TABLE test12 MODIFY salary DECIMAL(8,2);
    
    SHOW CREATE TABLE test12;
    
    • 1
    • 2
    • 3

    面试

    1. 面试1、为什么建表时,加 not null default ‘’ 或 default 0
      答:不想让表中出现null值。
    2. 面试2、为什么不想要 null 的值
      答:(1)不好比较。null是一种特殊值,比较时只能用专门的is null 和 is not null来比较。碰到运算符,通常返回null。
      (2)效率不高。影响提高索引效果。因此,我们往往在建表时 not null default ‘’ 或 default 0
    3. 面试3、带AUTO_INCREMENT约束的字段值是从1开始的吗? 在MySQL中,默认AUTO_INCREMENT的初始值是1,每新增一条记录,字段值自动加1。设置自增属性(AUTO_INCREMENT)的时候,还可以指定第一条插入记录的自增字段的值,这样新插入的记录的自增字段值从初始值开始递增,如在表中插入第一条记录,同时指定id值为5,则以后插入的记录的id值就会从6开始往上增加。添加主键约束时,往往需要设置字段自动增加属性。
    4. 面试4、并不是每个表都可以任意选择存储引擎?
      外键约束(FOREIGN KEY)不能跨引擎使用。即主表从表得使用同一个引擎
      MySQL支持多种存储引擎,每一个表都可以指定一个不同的存储引擎,需要注意的是:外键约束是用来保证数据的参照完整性的,如果表之间需要关联外键,却指定了不同的存储引擎,那么这些表之间是不能创建外键约束的。所以说,存储引擎的选择也不完全是随意的。
  • 相关阅读:
    MES在注塑制造领域的运用
    Android主流插件化
    大模型相关资料整理
    白杨SEO:如何拆解一个权6网站SEO优化方式和变现?我们做SEO可以学习哪些?
    Go命令行参数 os和flag
    13.10其他集合类(血干JAVA系类)
    仿哔哩哔哩微信小程序源码
    【前端】Vue实现个人空间
    Codeforces Round #818 (Div. 2) E. Madoka and The Best University(gcd性质+莫比乌斯反演φ)
    【软考软件评测师】第二十一章 计算机组成原理与体系结构(其他知识)
  • 原文地址:https://blog.csdn.net/CaraYQ/article/details/126543709