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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 这篇文章讨论了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”

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4月前
|
缓存 关系型数据库 MySQL
MySQL索引策略与查询性能调优实战
在实际应用中,需要根据具体的业务需求和查询模式,综合运用索引策略和查询性能调优方法,不断地测试和优化,以提高MySQL数据库的查询性能。
385 66
|
4月前
|
缓存 关系型数据库 MySQL
MySQL执行计划选择策略:揭秘查询优化的艺术
【10月更文挑战第15天】 在数据库性能优化中,选择最优的执行计划是提升查询效率的关键。MySQL作为一个强大的关系型数据库管理系统,提供了复杂的查询优化器来生成执行计划。本文将深入探讨如何选择合适的执行计划,以及为什么某些计划更优。
226 2
|
4月前
|
NoSQL 关系型数据库 MySQL
2024Mysql And Redis基础与进阶操作系列(4-2)作者——LJS[含MySQL非空、唯一性、PRIMARY KEY、自增列/自增约束举例说明等详解步骤及常见报错问题对应的解决方法]
24MySQL非空、唯一性、PRIMARY KEY、自增列/自增约束举例说明等详解步骤及常见报错问题对应的解决方法(4-2) 学不会你来砍我!!!
|
2月前
|
监控 关系型数据库 MySQL
Aurora MySQL负载突增应对策略与优化方案
通过以上策略,企业可以有效应对 Aurora MySQL 的负载突增,确保数据库在高负载情况下依然保持高性能和稳定性。这些优化方案涵盖了从架构设计到具体配置和监控的各个方面,能够全面提升数据库的响应速度和处理能力。在实际应用中,应根据具体的业务需求和负载特征,灵活调整和应用这些优化策略。
60 22
|
2月前
|
关系型数据库 MySQL 中间件
MySQL 中如何实现分库分表?常见的分库分表策略有哪些?
在MySQL中,分库分表(Sharding)通过将数据分散到多个数据库或表中,以应对大量数据带来的性能和扩展性问题。常见策略包括:哈希分片(分布均匀,查询效率高)、范围分片(适合范围查询)、列表分片(适用于特定值查询)、复合分片(灵活性高)和动态分片(灵活应对负载变化)。每种策略各有优劣,需根据业务需求选择。常用工具如MyCAT、ShardingSphere和TDDL可简化实现过程。
|
3月前
|
存储 SQL 关系型数据库
【MySQL基础篇】MySQL约束语法
文章介绍了MySQL中表的约束概念,包括非空、唯一、主键、默认和外键约束,以及如何在创建和修改表时指定这些约束。外键约束用于保持数据的一致性和完整性,文章通过示例展示了添加、删除外键的语法,并讨论了不同的删除/更新行为,如CASCADE和SETNULL。
【MySQL基础篇】MySQL约束语法
|
3月前
|
缓存 NoSQL 关系型数据库
MySQL战记:Count( *)实现之谜与计数策略的选择
本文深入探讨了MySQL中`count(*)`的不同实现方式,特别是MyISAM和InnoDB引擎的区别,以及各种计数方法的性能比较。同时,文章分析了使用缓存系统(如Redis)与数据库保存计数的优劣,并强调了在高并发场景下保持数据一致性的挑战。
120 0
MySQL战记:Count( *)实现之谜与计数策略的选择
|
4月前
|
SQL 关系型数据库 MySQL
PHP与MySQL的高效协同开发策略####
本文深入探讨了PHP与MySQL在Web开发中的协同工作机制,通过优化配置、最佳实践和高级技巧,展示了如何提升数据库交互性能,确保数据安全,并促进代码可维护性。我们将从环境搭建讲起,逐步深入到查询优化、事务管理、安全防护及性能调优等核心环节,为开发者提供一套实战驱动的解决方案框架。 ####
|
3月前
|
存储 关系型数据库 MySQL
MySQL(条件约束)
为了校验数据,让数据的正确性能够得到保证,约束,能够引进更多的检查操作,但是也会增加系统的成本开销
|
4月前
|
关系型数据库 MySQL 数据处理
MySQL函数与约束
MySQL 提供了丰富的函数和强大的约束机制,用于数据处理和完整性维护。通过掌握这些工具,可以有效地管理和分析数据库中的数据,确保数据的一致性和准确性。无论是在日常数据查询中使用内置函数,还是在数据库设计中应用各种约束,都是确保数据库系统稳定、高效运行的关键。希望本文对您理解和应用 MySQL 函数与约束有所帮助。
53 1