MySQL有哪些约束

    一、概述

    概念: 约束是作用于表中字段上的规则,用于限制存储在表中的数据。

    目的: 保证数据库中数据的正确、有效性和完整性。

    分类:

    MySQL有哪些约束

    注意:约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束。

    二、约束演示

    上面我们介绍了数据库中常见的约束,以及约束涉及到的关键字,那这些约束我们到底如何在创建表、修改表的时候来指定呢,接下来我们就通过一个案例,来演示一下。

    案例需求: 根据需求,完成表结构的创建。需求如下:

    对应的建表语句为:

    CREATE TABLE tb_user (
    id INT AUTO_INCREMENT PRIMARY KEY COMMENT '
    ID唯一标识'
    ,
    NAME VARCHAR ( 10 ) NOT NULL UNIQUE COMMENT '
    姓名'
    ,
    age INT CHECK ( age >
    0 &
    &
    age <
    = 120 ) COMMENT '
    年龄'
    ,
    STATUS CHAR ( 1 ) DEFAULT '
    1'
    COMMENT '
    状态'
    ,
    gender CHAR ( 1 ) COMMENT '
    性别'

    );

    在为字段添加约束时,我们只需要在字段之后加上约束的关键字即可,需要关注其语法。

    我们执行上面的SQL把表结构创建完成,然后接下来,就可以通过一组数据进行测试,从而验证一下,约束是否可以生效。

    (1)先是新增了三条数据

    insert into tb_user(name,age,status,gender) values ('
    Tom1'
    ,19,'
    1'
    ,'
    男'
    ),('
    Tom2'
    ,25,'
    0'
    ,'
    男'
    );

    insert into tb_user(name,age,status,gender) values ('
    Tom3'
    ,19,'
    1'
    ,'
    男'
    );

    新增三条数据,竟然用了21秒,这是什么情况?

    本来我还以为是新增这些约束导致新增数据慢的,其实不是,因为我这个是阿里的linux服务器,然后我在linux中通过客户端连接mysql执行新增,也就0.01秒,说明这是navicat连接远程主机耗时的。

    就算新增了这些约束,会导致新增数据慢,那也是批量的时候才能明显察觉出来,单条数据基本上看不出来的。

    (2)测试name NOT NULL

    insert into tb_user(name,age,status,gender) values (null,19,'
    1'
    ,'
    男'
    );

    (3)测试name UNIQUE(唯一)

    上面新增的数据已经有Tom3了,再次新增直接报错。

    insert into tb_user(name,age,status,gender) values ('
    Tom3'
    ,19,'
    1'
    ,'
    男'
    );

    虽然报错了,但是我们这时候再新增一条数据会发现一个现象。

    insert into tb_user(name,age,status,gender) values ('
    Tom4'
    ,80,'
    1'
    ,'
    男'
    );

    明明是自增id,但是却没有4,原因就是UNIQUE(唯一)是在申请完自增id后,准备入库了,然后这时候会先去看看库里面是否有存在相同name的值,如果有则新增失败,虽然新增失败了,但是自增id已经申请过了!

    相反我们刚刚测试的null的name的时候他并没有去申请id,因为他在刚开始就已经判断他为空了,还没走到申请id这一步。

    判断是否为空 -》 申请自增id -》 判断是否已经有存在的值

    总结:当新增的name不为空的时候,但是和之前存在的数据有相同的,这时候新增会失败,但是他会申请主键id。

    (4)测试CHECK

    我们设置的是age必须大于0小于等于120,否则保存失败!

    age int check (age >
    0 &
    &
    age <
    = 120) COMMENT '
    年龄'
    , insert into tb_user(name,age,status,gender) values ('
    Tom5'
    ,-1,'
    1'
    ,'
    男'
    );

    insert into tb_user(name,age,status,gender) values ('
    Tom5'
    ,121,'
    1'
    ,'
    男'
    );

    (5)测试 DEFAULT &
    lsquo;
    1&
    rsquo;
    默认值

    STATUS CHAR ( 1 ) DEFAULT '
    1'
    COMMENT '
    状态'
    , insert into tb_user(name,age,gender) values ('
    Tom5'
    ,120,'
    男'
    );

    (6)上面,我们是通过编写SQL语句的形式来完成约束的指定,那假如我们是Navicat客户端呢?

    主键自增

    name唯一约束

    status默认为1

    三、外键约束1、 什么是外键约束

    外键: 用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性。

    我们来看一个例子:

    左侧的emp表是员工表,里面存储员工的基本信息,包含员工的ID、姓名、年龄、职位、薪资、入职日期、上级主管ID、部门ID,在员工的信息中存储的是部门的ID dept_id,而这个部门的ID是关联的部门表dept的主键id,那emp表的dept_id就是外键,关联的是另一张表的主键。

    2、 不使用外键有什么影响

    通过上面的示例,我们分别来演示 添加外键 和不添加外键的区别,首先来看不添加 外键 对数据有什么影响:

    准备数据:

    CREATE TABLE dept ( id INT auto_increment COMMENT '
    ID'
    PRIMARY KEY, NAME VARCHAR ( 50 ) NOT NULL COMMENT '
    部门名称'
    ) COMMENT '
    部门表'
    ;


    INSERT INTO dept (id, name) VALUES (1, '
    研发部'
    ), (2, '
    市场部'
    ),(3, '
    财务部'
    ), (4, '
    销售部'
    ), (5, '
    总经办'
    );


    CREATE TABLE emp (
    id INT auto_increment COMMENT '
    ID'
    PRIMARY KEY,
    NAME VARCHAR ( 50 ) NOT NULL COMMENT '
    姓名'
    ,
    age INT COMMENT '
    年龄'
    ,
    job VARCHAR ( 20 ) COMMENT '
    职位'
    ,
    salary INT COMMENT '
    薪资'
    ,
    entrydate date COMMENT '
    入职时间'
    ,
    managerid INT COMMENT '
    直属领导ID'
    ,
    dept_id INT COMMENT '
    部门ID'

    ) COMMENT '
    员工表'
    ;


    INSERT INTO emp (id, name, age, job,salary, entrydate, managerid, dept_id) VALUES
    (1, '
    金庸'
    , 66, '
    总裁'
    ,20000, '
    2000-01-01'
    , null,5),
    (2, '
    张无忌'
    , 20, '
    项目经理'
    ,12500, '
    2005-12-05'
    , 1,1),
    (3, '
    杨逍'
    , 33, '
    开发'
    , 8400,'
    2000-11-03'
    , 2,1),
    (4, '
    韦一笑'
    , 48, '
    开 发'
    ,11000, '
    2002-02-05'
    , 2,1),
    (5, '
    常遇春'
    , 43, '
    开发'
    ,10500, '
    2004-09-07'
    , 3,1),
    (6, '
    小昭'
    , 19, '
    程 序员鼓励师'
    ,6600, '
    2004-10-12'
    , 2,1);

    接下来,我们可以做一个测试,删除id为1的部门信息。

    结果,我们看到删除成功,而删除成功之后,部门表不存在id为1的部门,而在emp表中还有很多的员工,关联的为id为1的部门,此时就出现了数据的不完整性。 而要想解决这个问题就得通过数据库的外键约束。

    正常开发当中有时候会通过业务代码来控制数据的不完整性,例如删除部门的时候会先根据部门id去查看一下有没有对应的员工表,如果有则删除失败,没有则删除成功。

    3、 添加外键的语法

    可以在创建表的时候直接添加外键,也可以对现已存在的表添加外键。

    (1)方式一

    CREATE TABLE 表名(
    字段名 数据类型,
    ...
    [CONSTRAINT] [外键名称] FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名)
    );

    使用示例:

    CREATE TABLE emp (
    id INT auto_increment COMMENT '
    ID'
    PRIMARY KEY,
    NAME VARCHAR ( 50 ) NOT NULL COMMENT '
    姓名'
    ,
    age INT COMMENT '
    年龄'
    ,
    job VARCHAR ( 20 ) COMMENT '
    职位'
    ,
    salary INT COMMENT '
    薪资'
    ,
    entrydate date COMMENT '
    入职时间'
    ,
    managerid INT COMMENT '
    直属领导ID'
    ,
    dept_id INT COMMENT '
    部门ID'
    ,
    CONSTRAINT fk_emp_dept_id FOREIGN KEY (dept_id) REFERENCES dept (id)
    ) COMMENT '
    员工表'
    ;

    也可以省略掉CONSTRAINT fk_emp_dept_id 这样mysql就会自动给我们起外键名称。

    方式二:对现存在的表添加外键

    ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名) ;

    使用示例:

    alter table emp add constraint fk_emp_dept_id FOREIGN KEY (dept_id) REFERENCES dept(id);

    方式三:Navicat添加外键

    删除外键:

    ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

    使用示例:

    alter table emp drop foreign key fk_emp_dept_id;
    4、 删除/更新行为

    我们将在父表数据删除时发生的限制行为称为删除/更新行为,此行为是在添加外键之后发生的。具体的删除/更新行为有以下几种:

    默认的MySQL 8.0.27版本中,RESTRICT是用于删除和更新行的行为!但是,不同的版本可能会有不同的行为

    具体语法为:

    ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名 (主表字段名) ON UPDATE CASCADE ON DELETE CASCADE;

    就是比原先添加外键后面多了这些ON UPDATE CASCADE ON DELETE CASCADE,代表的是更新时采用CASCADE ,删除时也采用CASCADE

    5、 演示删除/更新行为

    (1)演示RESTRICT

    在对父表中的记录进行删除或更新操作时,需要先检查该记录是否存在关联的外键,如果存在,则不允许执行删除或更新操作。 (与 NO ACTION 一致) 默认行为

    首先要添加外键,默认是RESTRICT行为!

    alter table emp add constraint fk_emp_dept_id FOREIGN KEY (dept_id) REFERENCES dept(id);

    删除父表中id为5的记录时,会因为emp表中的dept_id存在5而报错。假如要更新id也同样会报错的!

    (2)演示CASCADE

    当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则

    也删除/更新外键在子表中的记录。

    删除外键的语法:

    ALTER TABLE 表名 DROP FOREIGN KEY 外键约束名;

    删除外键的示例:

    ALTER TABLE emp DROP FOREIGN KEY fk_emp_dept_id;

    指定外键的删除更新行为为cascade

    alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update cascade on delete cascade ;

    修改父表id为1的记录,将id修改为6

    我们发现,原来在子表中dept_id值为1的记录,现在也变为6了,这就是cascade级联的效果。

    在一般的业务系统中,不会修改一张表的主键值。

    删除父表id为6的记录

    我们发现,父表的数据删除成功了,但是子表中关联的记录也被级联删除了。

    (3)演示SET NULL

    当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(这就要求该外键允许取null)。

    alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update set null on delete set null ;

    在执行测试之前,我们需要先移除已创建的外键 fk_emp_dept_id。然后再通过数据脚本,将emp、dept表的数据恢复了。

    接下来,我们删除id为1的数据,看看会发生什么样的现象。

    我们发现父表的记录是可以正常的删除的,父表的数据删除之后,再打开子表 emp,我们发现子表emp的dept_id字段,原来dept_id为1的数据,现在都被置为NULL了。

    这就是SET NULL这种删除/更新行为的效果。

    四、主键id到底用自增好还是uuid好

    在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究竟有什么坏处?

    1、测试uuid和自增id还有随机数插入效率

    首先来建立三张表,user_auto_key代表的是自增表,user_uuid代表的是id存储的uuid,random_key代表的是表id是雪花id。然后通过连接jdbc批量插入数据测试测试结果如下:

    在已有数据量为130W的时候:我们再来测试一下插入10w数据,看看会有什么结果:

    可以看出在数据量100W左右的时候,uuid的插入效率垫底,并且在后序增加了130W的数据,uudi的时间又直线下降。时间占用量总体可以打出的效率排名为:auto_key>
    random_key>
    uuid,uuid的效率最低

    2、使用自增id的缺点

    1.别人一旦爬取你的数据库,就可以根据数据库的自增id获取到你的业务增长信息,很容易分析出你的经营情况

    2.对于高并发的负载,innodb在按主键进行插入的时候会造成明显的锁争用,主键的上界会成为争抢的热点,因为所有的插入都发生在这里,并发插入会导致间隙锁竞争

    3.Auto_Increment锁机制会造成自增锁的抢夺,有一定的性能损失

    4.自增id涉及到数据迁移的话是相当麻烦的!

    5.而且一旦涉及到分库分表自增id也是相当麻烦的!

    3、使用uuid的缺点

    因为uuid相对顺序的自增id来说是毫无规律可言的,新行的值不一定要比之前的主键的值要大,所以innodb无法做到总是把新行插入到索引的最后,而是需要为新行寻找新的合适的位置从而来分配新的空间。这个过程需要执行多个额外操作,而数据的无序可能会导致数据分散,从而产生以下问题:

    1.写入的目标页很可能已经刷新到磁盘上并且从缓存上移除,或者还没有被加载到缓存中,innodb在插入之前不得不先找到并从磁盘读取目标页到内存中,这将导致大量的随机IO

    2.因为写入是乱序的,innodb不得不频繁的做页分裂操作,以便为新的行分配空间,页分裂导致移动大量的数据,一次插入最少需要修改三个页以上

    3.由于频繁的页分裂,页会变得稀疏并被不规则的填充,最终会导致数据会有碎片

    页分裂和碎片问题,uuid确实会引起这个问题,但雪花可以解决这个问题,雪花算法天然具有顺序性新插入的ID一定是最大的,所以我认为用雪花算法是一个很不错的选择!

    五、实际开发尽量少用外键

    主键和索引是不可少的,不仅可以优化数据检索速度,开发人员还省不其它的工作。

    矛盾焦点:数据库设计是否需要外键。这里有两个问题:

    一个是如何保证数据库数据的完整性和一致性;

    二是第一条对性能的影响。

    这里分为了正方和反方两个观点,供参考!

    1、正方观点

    1.由数据库自身保证数据一致性,完整性,更可靠,因为程序很难100%保证数据的完整性,而用外键即使在数据库服务器当机或者出现其他问题的时候,也能够最大限度的保证数据的一致性和完整性。

    2.有主外键的数据库设计可以增加ER图的可读性,这点在数据库设计时非常重要。

    3.外键在一定程度上说明的业务逻辑,会使设计周到具体全面。

    数据库和应用是一对多的关系,A应用会维护他那部分数据的完整性,系统一变大时,增加了B应用,A和B两个应用也许是不同的开发团队来做的。如何协调以确保数据完整性,并且如果在一年后添加了新的C应用,又该如何处理?

    2、反方观点

    1.可以用触发器或应用程序保证数据的完整性

    2.过分强调或者说使用主键/外键会平添开发难度,导致表过多等问题

    3.不用外键时数据管理简单,操作方便,性能高(导入导出等操作,在insert, update, delete 数据的时候更快)

    在海量的数据库中想都不要去想外键,试想,一个程序每天要insert数百万条记录,当存在外键约束的时候,每次要去扫描此记录是否合格,一般还不 止一个字段有外键,这样扫描的数量是成级数的增长!我的一个程序入库在3个小时做完,如果加上外键,需要28个小时!

    3、结论

    1.在大型系统中(性能要求不高,安全要求高),使用外键;在大型系统中(性能要求高,安全自己控制),不用外键;小系统随便,最好用外键。

    2.用外键要适当,不能过分追求

    为了保证数据的一致性和完整性,可以不使用外键而通过程序进行控制。这时应编写一层来实现数据保护,再通过这个层来访问数据库的各个应用程序。

    需要注意的是:

    MySQL允许使用外键,但是为了完整性检验的目的,在除了InnoDB表类型之外的所有表类型中都忽略了这个功能。这可能有些怪异,实际上却非常正常:对于数据库的所有外键的每次插入、更新和删除后,进行完整性检查是一个耗费时间和资源的过程,它可能影响性能,特别是当处理复杂的或者是缠绕的连接数时。因而,用户可以在表的基础上,选择适合于特定需求的。

    所以,如果需要更好的性能,并且不需要完整性检查,可以选择使用MyISAM表类型,如果想要在MySQL中根据参照完整性来建立表并且希望在此基础上保持良好的性能,最好选择表结构为innoDB类型



    MySQL是一个非常强大的关系型数据库管理系统,支持许多各式各样的约束,每个约束的唯一目的就是确保数据的完整性和一致性。无论是初学者还是经验丰富的数据库管理员,都应该清楚地了解MySQL中所有可用的约束。
    一、常见的约束类型
    在MySQL中,约束可以分为多种类型,例如PRIMARY KEY、FOREIGN KEY、UNIQUE、NOT NULL、CHECK等等。其中,PRIMARY KEY是最常见的一种,它用于定义一个表中的主键。而FOREIGN KEY则能够定义一个表之间的关系,UNIQUE则确保某些数据不能重复,NOT NULL强制某个字段不能为空,而CHECK则可以进行复杂的数据限制。
    二、为什么约束非常重要?
    遵循约束可以确保数据库中数据的完整性和一致性。当您指定一个字段不能为NULL时,将确保该字段不会在任何记录中为空。这样做可以防止您意外地填入了无效的数据,并干扰您的应用程序,甚至崩溃您的整个系统。约束还可以帮助您识别数据的重复、错误或不允许的值。总之,遵循约束可以确保您的数据库始终处于正确的状态。
    三、如何使用约束?
    使用约束需要遵循一些规则。例如,PRIMARY KEY约束应该在表中定义一个唯一的主键,而FOREIGN KEY约束应该在两个表之间定义外键。不遵循这些规则将会导致约束无法工作,或者连接和查询发生错误。但您可以通过使用ALTER TABLE语句来更改表中的约束,让您能够根据需要更改或删除约束。
    结论
    MySQL中的约束是数据库的一个重要组成部分。不遵循约束往往会导致错误、重复或不一致,最终可能造成您的整个系统崩溃。因此,为了确保数据的完整性和一致性,您应该了解不同类型的约束,并在创建和管理表时使用它们。这样做可以减少错误,提高生产力,为用户提供一个更好的数据库体验。