MySQL中外键的使用及外键约束策略

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 这篇文章讨论了MySQL中使用外键的重要性,包括外键的概念、不使用外键可能导致的问题、如何设置外键约束以及不同的外键约束策略(如CASCADE和SET NULL),并通过示例演示了这些概念。

一、外键约束的概念

外键约束(FOREIGN KEY,缩写FK是数据库设计的一个概念,它确保在两个表之间的关系保持数据的一致性和完整性。

外键是指表中的某个字段的依赖于另一张表中某个字段的值,而被依赖的字段必须具有主键约束或者唯一约束,被依赖的数据表称为主表(父表),设置外键约束的表称为子表或者从表。

比方说:现在有两张表,学生表和班级表,学生表中的班级号字段的值取决于班级表中班级号字段。
在这里插入图片描述

主表(父表):班级表-班级编号-主键
从表(子表):学生表-班级编号-外键

二、不使用外键约束会出现的问题

首先创建这两张表,学生表和班级表,然后不添加班级号的外键约束,并且插入一些初始化数据。

(一)创建表及初始化数据

-- 创建班级表
create table t_class(
    cno int(4) primary key auto_increment, -- 班级号自增主键
    cname varchar(12) not null -- 班级名称不为空值
);

-- 查看班级表
select * from t_class;

-- 插入班级信息
insert into t_class values (null,'java01班') ,(null,'python01班'),(null,'大数据01班');


-- 创建学生表(不含有外键约束的)
create table t_student(
    sno int(4) primary key auto_increment, -- 学号为主键自增
    sname varchar(5) not null , -- 姓名为非空约束
    age int(3) check (age >= 18 and age <= 55), -- 年龄为18-55之间 检查约束
    sex char(1) default '男' check (sex = '男' || sex = '女'), -- 性别默认为男,只能是男或者女
    cno int(4) 
);

-- 查看学生表
select * from t_student;

-- 插入学生信息  编号从1001开始
insert into t_student values (1001,'张三',21,'男',1); 
insert into t_student values (null,'李四',21,'男',1); 
insert into t_student values (null,'王五',21,'男',2);
insert into t_student values (null,'赵六',21,'男',3);
insert into t_student values (null,'崔七',21,'男',4); # 插入不存在的班级编号依旧成功
delete from t_student where cno = 4; -- 删除掉错误的数据

在没有外键约束的情况下,其实学生表(从表)中的班级号是可以任意插入的,造成了数据的不一致性

(二)更新和删除表数据

  1. 更新java01班的班级号为9,学生表中的数据并没有进行更新,数据再次不一致
-- 更新班级表数据
-- 将java01班的编号修改为9号
update t_class set cno = 9 where cname = 'java01班' and cno = 1;
select * from t_class; -- 班级表中成功修改
select * from t_student; -- 学生表中java01班的学生的班级编号并没有修改为9
  1. 删除java01班的班级信息,学生表中的java01班的学生信息依旧没有变化,数据不一致性
-- 删除表数据
-- 删除java01班的班级信息
delete from t_class where cno = 9 ;
select * from t_class;
select * from t_student;

(三)小结

在不使用外键约束的情况下,增删改均会影响数据的不一致性和完整性。

三、使用外键约束及外键策略

(一)创建表及初始化数据

-- 创建班级表
create table t_class(
    cno int(4) primary key auto_increment,
    cname varchar(12) not null
);

-- 查看班级表
select * from t_class;

-- 插入班级信息
insert into t_class values (null,'java01班') ,(null,'python01班'),(null,'大数据01班');

-- 创建学生表 外键约束只有表级约束
create table t_student(
    sno int(4) primary key auto_increment, -- 学号为主键自增
    sname varchar(5) not null , -- 姓名为非空约束
    age int(3) check (age >= 18 and age <= 55), -- 年龄为18-55之间 检查约束
    sex char(1) default '男' check (sex = '男' || sex = '女'), -- 性别默认为男,只能是男或者女
    cno int(4),
  constraint fk_stu_classcno    foreign key (cno) references t_class (cno) -- 添加外键约束
);

-- 查看学生表
select * from t_student;

-- 插入学生信息  1.(在没有外键约束的情况下,其实我们插入任何班级号都是可以的)
insert into t_student values (1001,'张三',21,'男',1); -- 这里第一个插入的id会影响后面的id,从1001开始
insert into t_student values (null,'李四',21,'男',1); 
insert into t_student values (null,'王五',21,'男',2);
insert into t_student values (null,'赵六',21,'男',3);
-- > 1452 - Cannot add or update a child row: a foreign key constraint fails (`mytestdb`.`t_student`, CONSTRAINT `fk_stu_classcno` FOREIGN KEY (`cno`) REFERENCES `t_class` 
insert into t_student values (null,'崔七',21,'男',4); # 插入不存在的班级编号报错

这里插入不存在的班级编号时,直接保存,因为外键约束帮我们做出了限制.

1452 - Cannot add or update a child row: a foreign key constraint fails (mytestdb.t_student, CONSTRAINT fk_stu_classcno FOREIGN KEY (cno) REFERENCES t_class

(二) 更新和删除数据

更新和删除数据时,涉及到外键策略,我们先尝试着去改动和删除一些数据。

  1. 更新java01班的班级号为9
update t_class set cno = 9 where cno = 1;

报错:update t_class set cno = 9 where cno = 1
1451 - Cannot delete or update a parent row: a foreign key constraint fails (mytestdb.t_student, CONSTRAINT fk_stu_classcno FOREIGN KEY (cno) REFERENCES t_class (cno))
时间: 0.002s

因为添加了外键约束,两个表已经建立了关系,为了维持数据的一致性,当改动班级表中的班级号时,原有的学生表中的编号也需要改动,默认情况下是不执行的,需要添加一些条件。

  1. 删除java01班的班级信息
delete from t_class where cno = 1;

报错:delete from t_class where cno = 1
1451 - Cannot delete or update a parent row: a foreign key constraint fails (mytestdb.t_student, CONSTRAINT fk_stu_classcno FOREIGN KEY (cno) REFERENCES t_class (cno))
时间: 0.008s
和更新时的报错原因一样,同样是因为外键的原因。

(三)外键策略

tips:cascade 操作 和 set null 操作 在表的创建添加外键约束时即可添加,我这里是为了方便直接修改外键约束策略了。灵活的根据业务将set null 和 cascade 结合起来使用

  1. no action 不允许操作–默认的外键策略 (修改从表的数据为Null,然后再修改主表的数据) 这种方法比较傻,就是硬写SQL
-- 修改java01班的班级编号为9
-- 1. 先修改学生表中java01班的数据为null
update t_student set cno = null where cno = 1;
-- 2. 更新班级表中的数据
update t_class set cno = 9 where cno = 1;
-- 3. 更新学生表中的数据
update t_student set cno = 9 where sno in (1001,1002);
  1. cascade 级联操作,操作主表的时候影响从表的外键信息 (先删除之前的外键约束再重新添加外键约束)。
-- 1.删除原有外键约束
alter table t_student drop foreign key fk_stu_classcno;
-- 2.添加新的外键约束策略  --- 在更新和删除时进行级联操作
alter table t_student add constraint fk_stu_classcno foreign key (cno) references t_class (cno) on update cascade on delete cascade;
-- 3. 更新数据 将java01班的班级号换为 19
update t_class set cno = 19 where cno = 9;
-- 4. 查询验证
select * from t_class;
select * from t_student;
  1. set null 置空操作,操作主表的时候影响从表的外键信息,从表对应的值为null值(先删除之前的外键约束然后重新添加新的外键约束)。
-- 1.删除原有外键约束
alter table t_student drop foreign key fk_stu_classcno;
-- 2.添加新的外键约束策略 --- 更新和删除时将从表的外键值都置为null
alter table t_student add constraint fk_stu_classcno foreign key (cno) references t_class (cno) on update set null  on delete set null;
-- 3. 更新数据 将java01班的班级号换为 29
update t_class set cno = 29 where cno = 19;
-- 4. 手动更新学生从表的数据
update t_student set cno = 29 where sno in (1001,1002);
-- 4. 查询验证
select * from t_class;
select * from t_student;

下面是一个在创建表时,添加外键约束策略的例子:

-- 创建班级表
create table t_class(
    cno int(4) primary key auto_increment,
    cname varchar(12) not null
);

-- 插入班级信息
insert into t_class values (null,'java01班') ,(null,'python01班'),(null,'大数据01班');

-- 查看班级表
select * from t_class;

-- 创建学生表 外键约束只有表级约束
create table t_student(
    sno int(4) primary key auto_increment, -- 学号为主键自增
    sname varchar(5) not null , -- 姓名为非空约束
    age int(3) check (age >= 18 and age <= 55), -- 年龄为18-55之间 检查约束
    sex char(1) default '男' check (sex = '男' || sex = '女'), -- 性别默认为男,只能是男或者女
    cno int(4),
    -- 当主表中的数据更新时,从表数据级联更新,当主表数据删除时,从表数据设置为Null
  constraint fk_stu_classcno    foreign key (cno) references t_class (cno) on update cascade on delete set null
);

-- 插入学生信息  1.(在没有外键约束的情况下,其实我们插入任何班级号都是可以的)
insert into t_student values (1001,'张三',21,'男',1); -- 这里第一个插入的id会影响后面的id,从1001开始
insert into t_student values (null,'李四',21,'男',1); 
insert into t_student values (null,'王五',21,'男',2);
insert into t_student values (null,'赵六',21,'男',3);

-- 查看学生表
select * from t_student;

-- 更新java01班班级编号为9
update t_class set cno = 9 where cno = 1;
select * from t_class;
select * from t_student;

-- 删除大数据01班
delete from t_class where cno = 3;
select * from t_class;
select * from t_student;

四、总结

当使用外键约束时,需要注意以下几点:

  • 外键约束只有表级约束,没有列级约束
  • 外键约束会影响表的性能,因为数据库必须对每个写操作执行额外的检查
  • 如果尝试插入不符合外键约束的行,数据库会抛出一个错误
  • 根据不同的业务需求自定义不同的外键策略 (cascade || set null)“constraint fk_stu_classcno foreign key (cno) references t_class (cno) on update cascade on delete set null”

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
4月前
|
存储 安全 关系型数据库
MySQL数据库约束条件概述及其重要性讨论。
正确地实现并管理好各类紧缩条件将直接影响到企业信息管理水平与服务质量,在当今大数据背景下更显得格外重要;任何设计师都需要深刻理解其原理与运作机晰承担起责任使得所托管资料安全稳固同时又具备良好伸缩灵活度迎合日益复杂商务需求变动.
189 11
|
4月前
|
缓存 关系型数据库 MySQL
降低MySQL高CPU使用率的优化策略。
通过上述方法不断地迭代改进,在实际操作中需要根据具体场景做出相对合理判断。每一步改进都需谨慎评估其变动可能导致其他方面问题,在做任何变动前建议先在测试环境验证其效果后再部署到生产环境中去。
232 6
|
缓存 关系型数据库 MySQL
MySQL索引策略与查询性能调优实战
在实际应用中,需要根据具体的业务需求和查询模式,综合运用索引策略和查询性能调优方法,不断地测试和优化,以提高MySQL数据库的查询性能。
633 66
|
5月前
|
缓存 关系型数据库 MySQL
在MySQL中处理高并发和负载峰值的关键技术与策略
采用上述策略和技术时,每个环节都要进行细致的规划和测试,确保数据库系统既能满足高并发的要求,又要保持足够的灵活性来应对各种突发的流量峰值。实施时,合理评估和测试改动对系统性能的影响,避免单一措施可能引起的连锁反应。持续的系统监控和分析将对维护系统稳定性和进行未来规划提供重要信息。
318 15
|
4月前
|
缓存 关系型数据库 MySQL
MySQL数据库性能调优:实用技术与策略
通过秉持以上的策略实施具体的优化措施,可以确保MySQL数据库的高效稳定运行。务必结合具体情况,动态调整优化策略,才能充分发挥数据库的性能潜力。
217 0
|
缓存 关系型数据库 MySQL
MySQL执行计划选择策略:揭秘查询优化的艺术
【10月更文挑战第15天】 在数据库性能优化中,选择最优的执行计划是提升查询效率的关键。MySQL作为一个强大的关系型数据库管理系统,提供了复杂的查询优化器来生成执行计划。本文将深入探讨如何选择合适的执行计划,以及为什么某些计划更优。
383 2
|
NoSQL 关系型数据库 MySQL
2024Mysql And Redis基础与进阶操作系列(4-2)作者——LJS[含MySQL非空、唯一性、PRIMARY KEY、自增列/自增约束举例说明等详解步骤及常见报错问题对应的解决方法]
24MySQL非空、唯一性、PRIMARY KEY、自增列/自增约束举例说明等详解步骤及常见报错问题对应的解决方法(4-2) 学不会你来砍我!!!
|
8月前
|
关系型数据库 MySQL 大数据
大数据新视界--大数据大厂之MySQL 数据库课程设计:MySQL 数据库 SQL 语句调优的进阶策略与实际案例(2-2)
本文延续前篇,深入探讨 MySQL 数据库 SQL 语句调优进阶策略。包括优化索引使用,介绍多种索引类型及避免索引失效等;调整数据库参数,如缓冲池、连接数和日志参数;还有分区表、垂直拆分等其他优化方法。通过实际案例分析展示调优效果。回顾与数据库课程设计相关文章,强调全面认识 MySQL 数据库重要性。为读者提供综合调优指导,确保数据库高效运行。
|
11月前
|
监控 关系型数据库 MySQL
Aurora MySQL负载突增应对策略与优化方案
通过以上策略,企业可以有效应对 Aurora MySQL 的负载突增,确保数据库在高负载情况下依然保持高性能和稳定性。这些优化方案涵盖了从架构设计到具体配置和监控的各个方面,能够全面提升数据库的响应速度和处理能力。在实际应用中,应根据具体的业务需求和负载特征,灵活调整和应用这些优化策略。
218 22
|
11月前
|
关系型数据库 MySQL 中间件
MySQL 中如何实现分库分表?常见的分库分表策略有哪些?
在MySQL中,分库分表(Sharding)通过将数据分散到多个数据库或表中,以应对大量数据带来的性能和扩展性问题。常见策略包括:哈希分片(分布均匀,查询效率高)、范围分片(适合范围查询)、列表分片(适用于特定值查询)、复合分片(灵活性高)和动态分片(灵活应对负载变化)。每种策略各有优劣,需根据业务需求选择。常用工具如MyCAT、ShardingSphere和TDDL可简化实现过程。

推荐镜像

更多