约束时一种限制,用于限制表中的数据,为了保证表中的数据的准确可靠性。
约束的分类:

  • NOT NULL:非空约束,用于保证该字段的值不能为空,比如姓名、学号 DEFAULT:默认,用于保证该字段有默认值,比如性别
  • PRIMARY KEY:主键,用于保证该字段的值具有唯一性
  • UNIQUE:唯一,用于抱枕该字段的值具有唯一性,可以为空
  • CHECK:检查约束(MySQL中不支持),比如限定年龄的范围
  • FOREIGN KEY:外键约束,用于限定两个表的关系,用于保证该字段的值必须来自于主表的关联列的值。在从表中添加外键约束,引用从表的字段中的值。

添加约束的时机

  • 建表时添加约束
  • 建表之后,数据添加之前,添加约束

约束的添加分类

  • 列级约束:六大约束都可以写,但外键约束没有效果
  • 表级约束:除了非空和默认,其它的都支持

    7.1 创建表时添加约束

    语法: ```sql CREATE TABLE stu_info( id INT PRIMARY KEY,# 唯一约束 stuName VARCHAR(20) NOT NULL ,# 非空约束 gender char(1) CHECK(gender=’男’ OR gender = ‘女’),#检查约束 stu_phone char(11) UNIQUE,#唯一约束 age INT DEFAULT 18 ,# 默认约束 major_id INT REFERENCES major(id)# 外键约束

) CREATE TABLE major( id INT PRIMARY KEY, majorName VARCHAR(20) ); DESC stu_info;

  1. > 直接在字段名和类型后面追加约束类型即可。
  2. > 只支持:默认、非空、主键、唯一。
  3. > 外键约束添加在列上没有意义和效果,检查约束MySQL不支持。
  4. <a name="o3LjA"></a>
  5. # 7.2 添加表级约束
  6. **语法:**
  7. ```sql
  8. CREATE TABLE stuinfo(
  9. id INT,
  10. stuname VARCHAR(20),
  11. gender char(1),
  12. seat INT,
  13. age INT,
  14. majorid int,
  15. ## 开始添加约束
  16. CONSTRAINT pk primary KEY(id),#主键
  17. CONSTRAINT uq UNIQUE(seat),#唯一键
  18. CONSTRAINT ck CHECK(gender='男' OR gender='女'),
  19. CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id) #外键
  20. )

可以省略约束的名字,系统会按照规则赋予一个名字

  1. CREATE TABLE stuinfo(
  2. id INT,
  3. stuname VARCHAR(20),
  4. gender char(1),
  5. seat INT,
  6. age INT,
  7. majorid int,
  8. ## 开始添加约束
  9. primary KEY(id),#主键
  10. UNIQUE(seat),#唯一键
  11. CHECK(gender='男' OR gender='女'),
  12. FOREIGN KEY(majorid) REFERENCES major(id) #外键
  13. )

表级约束不支持非空和默认

总结

  1. # 在各个字段的最下面
  2. constraint 约束名】 约束类型(字段名)

7.3 通用的写法

  1. CREATE TABLE IF NOT EXISTS stuinfo(
  2. id int primary key,
  3. stuname varchar(20) NOT NULL,
  4. gender char(1),
  5. seat INT UNIQUE,
  6. majorid int,
  7. CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)
  8. )

外键一般用表级约束,其他的约束一般用列级约束。

7.4 逐渐约束和唯一约束的对比

  1. 主键唯一非空;唯一约束,唯一,但是可以为空。且可以有多个空值(MySQL 5.7)
  2. 主键一张表中只能有一个;唯一约束一个表中可以有多个
  3. 可以有联合主键和联合唯一键。

    1. CREATE TABLE stuinfo(
    2. id INT,
    3. stuname VARCHAR(20),
    4. gender char(1),
    5. seat INT,
    6. seat2 INT,
    7. age INT,
    8. ## 开始添加约束
    9. primary KEY(id,stuname),#主键
    10. UNIQUE(seat,seat2),#唯一键
    11. CHECK(gender='男' OR gender='女')
    12. )

    联合唯一约束中,null值还是可以重复,不论是一个null还是两个null。 联合主键中,必须两个一摸一样才会被限制插入,不然就视为不重复。

7.5 外键的特点

  1. 要求在从表设置外键关系
  2. 从表的外键列的类型和主表的关联列的类型要求一致或兼容
  3. 主表的关联列必须是一个key(主键约束的字段、唯一约束的字段),也就是关联唯一。
  4. 插入数据时,应该先插入主表,再插入从表(拿 专业—学生 举例,先添加专业才能添加专业中所属的学生)
  5. 删除数据时,先删除从表,再删除主表。

问题:为什么实际的业务场景中,不适合使用外键约束?
引言
其实这个话题是老生常谈,很多人在工作中确实也不会使用外键。包括在阿里的JAVA规范中也有下面这一条

【强制】不得使用外键与级联,一切外键概念必须在应用层解决。

但是呢,询问他们原因,大多是这么回答的

每次做DELETE 或者UPDATE都必须考虑外键约束,会导致开发的时候很痛苦,测试数据极为不方便。

坦白说,这么说也是对的。但是呢,不够全面,所以开一文来详细说明。
正文
首先我们明确一点,外键约束是一种约束,这个约束的存在,会保证表间数据的关系“始终完整”。因此,外键约束的存在,并非全然没有优点。
比如使用外键,可以

  • 保证数据的完整性和一致性
  • 级联操作方便
  • 将数据完整性判断托付给了数据库完成,减少了程序的代码量

然而,鱼和熊掌不可兼得。外键是能够保证数据的完整性,但是会给系统带来很多缺陷。正是因为这些缺陷,才导致我们不推荐使用外键,具体如下

性能问题

假设一张表名为user_tb。那么这张表里有两个外键字段,指向两张表。那么,每次往user_tb表里插入数据,就必须往两个外键对应的表里查询是否有对应数据。如果交由程序控制,这种查询过程就可以控制在我们手里,可以省略一些不必要的查询过程。但是如果由数据库控制,则是必须要去这两张表里判断。

并发问题

在使用外键的情况下,每次修改数据都需要去另外一个表检查数据,需要获取额外的锁。若是在高并发大流量事务场景,使用外键更容易造成死锁。

扩展性问题

这里主要是分为两点

  • 做平台迁移方便,比如你从Mysql迁移到Oracle,像触发器、外键这种东西,都可以利用框架本身的特性来实现,而不用依赖于数据库本身的特性,做迁移更加方便。
  • 分库分表方便,在水平拆分和分库的情况下,外键是无法生效的。将数据间关系的维护,放入应用程序中,为将来的分库分表省去很多的麻烦。

    技术问题

    使用外键,其实将应用程序应该执行的判断逻辑转移到了数据库上。那么这意味着一点,数据库的性能开销变大了,那么这就对DBA的要求就更高了。很多中小型公司由于资金问题,并没有聘用专业的DBA,因此他们会选择不用外键,降低数据库的消耗。
    相反的,如果该约束逻辑在应用程序中,发现应用服务器性能不够,可以加机器,做水平扩展。如果是在数据库服务器上,数据库服务器会成为性能瓶颈,做水平扩展比较困难。

6. 修改表时添加约束

语法

  1. #列级约束
  2. ALTER TABLE 表名 MODIFY COLUMN 列名 列类型 约束;
  3. #表级约束
  4. ALTER TABLE 表名 ADD 约束名(列名);

举例

  1. CREATE TABLE stuinfo(
  2. id INT,
  3. stuname VARCHAR(20),
  4. gender char(1),
  5. seat INT
  6. )
  7. #列级约束的写法
  8. ALTER TABLE stuinfo MODIFY COLUMN id INT PRIMARY KEY;
  9. ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) UNIQUE;
  10. #表级约束的写法
  11. ALTER TABLE stuinfo ADD UNIQUE(seat);
  12. # 添加外键,添加外键时,必须保证主表存在,并且关联的字段是唯一字段,并且类型一致或者兼容
  13. ALTER TABLE stuinfo ADD
  14. CONSTRAINT fk_stuinfo_major
  15. FOREIGN KEY(majorid) REFERENCES major(id);

7. 删除约束

语法

  1. ##删除非空
  2. ALTER TABLE stuinfo MODIFY stuname VARCHAR(20) null;
  3. ## 删除默认约束
  4. ALTER TABLE stuinfo MODIFY COLUMN age int;
  5. ##删除主键
  6. ALTER TABLE stuinfo DROP PRIMARY KEY;
  7. ## 删除唯一键
  8. ALTER TABLE stuinfo DROP INDEX seat;
  9. ## 删除外键约束
  10. ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major

7.6 标识列

又称为自增长列 含义:可以不用手动的插入值,系统提供默认的序列值

创建表时设置标识列

  1. CREATE TABLE stuinfo(
  2. id INT PRIMARY KEY auto_increment,
  3. stuname VARCHAR(20),
  4. gender char(1),
  5. seat INT
  6. );
  7. INSERT INTO stuinfo VALUES(null,'l1','男',10);
  8. INSERT INTO stuinfo VALUES(null,'l2','男',10);
  9. INSERT INTO stuinfo VALUES(null,'l3','男',10);
  10. INSERT INTO stuinfo VALUES(null,'l4','男',10);

设置自增长的步长

  1. # 查看自增长的起始值 offset 和步长 increment
  2. SHOW VARIABLES LIKE '%auto_increment%';
  3. # 起始值不可被修改,但是步长可以被修改
  4. SET auto_increment_increment=3;

需要删除之前表中的全部数据,才会有这样的效果。

特点:

  1. 标识列必须是一个键,主键或者是唯一键
  2. 仅仅只能有一个增长列
  3. 标识列的类型必须是数值类型(小数和整数都可以)
  4. 标识列可以设置步长,但是不支持设置起始值,不过可以通过手动插入第一条数据的时候给予值的形式来给定值。

修改表时设置标识列

  1. ALTER TABLE stuinfo MODIFY COLUMN id INT auto_increment;

修改表时删除标识列

  1. ALTER TABLE stuinfo MODIFY COLUMN id INT;