MySQL表的增删查改(3)——复杂情况

1. 表的设计

三大范式:

一对一:如一个学生可以有一个学号,一个学号只能被一个学生拥有

一对多:如一个学生只能属于一个班级,但一个班级可以有多个学生(这种方案在MySQL中是不可行的,但在有些数据库(如Redis中)能够支持列表,如下图:

多对多:如一个学生可以选择多门课程,一个课程可以被多个学生选择

2. 新增

插入查询结果(即将表1的查询结果插入到表2中,表1得到的结果集合(列数、类型、顺序)要和insert into后面的表2相匹配,列的名字不要求相同)

语法:

INSERT INTO table_name [(column [, column ...])] SELECT ...

示例:

--只有表名不同的两个表,将表1中数据根据筛选条件新增到表2中
create table student(id int, name varchar(20));
create table student2(id int, name varchar(20));
insert into student values (1,'zhangsan'),(2,'lisi'),(3,'wangwu'),(100,'zhaoliu');
insert into student2 select * from student where id < 50;


--表明不同,列名不同的情况,也可以成功新增
create table student(id int, name varchar(20));
create table student2(classId int, className varchar(20));


--表名不同,列名类型相同,但顺序不同
create table student(id int, name varchar(20));
create table student2(name varchar(20), id int);
insert into student2(id, name) select * from student; --两种方法均可
insert into student2 select name, id from student;

3. 查询

3.1 聚合查询

3.1.1 聚合函数

常见的统计总数、计算平均值等操作可以使用聚合函数来实现,常见的聚合函数:

函数说明
COUNT([DISTINCT] expr)返回查询到的数据的 数量
SUM([DISTINCT] expr)返回查询到的数据的 总和,不是数字没有意义
AVG([DISTINCT] expr)返回查询到的数据的 平均值,不是数字没有意义
MAX([DISTINCT] expr)返回查询到的数据的 最大值,不是数字没有意义
MIN([DISTINCT] expr)返回查询到的数据的 最小值,不是数字没有意义

示例:

count
--统计班级里共有多少同学
select count(*) from student;
select count(0) from student;

--统计班级收集到的qq_mail有多少个,为NULL的数据不会计入结果
select count(qq_mail) from student;


 sum
--统计数学成绩总分
select sum(math) from exam_result;

--不及格(<60)同学的总分,没有结果返回null
select sum(math) from exam_result where math < 60;

tip:需要确保进行求和的列是数字,如果是字符串就会出现问题

虽然能将是数字的字符串转换出来,但是原则上不应该对字符串进行求和操作,如:字符串类型的手机号/身份证号...这些转换成数字进行求和操作是无意义的


avg
--计算平均总分
select avg(chinese + math + english) 平均总分 from exam_result;
max
--返回英语最高分
select max(english) from exam_result;
min
--返回 >70 分以上的数学最低分
select min(math) from exam_result where math > 70;

3.1.2 group by 子句

使用 group by 指定一个列,就会把 列的值 相同的 行 归到一组中,分完组之后,还可以针对每个组分别进行聚合查询

语法:
select column1, sum(column2), .. from table group by column1,column3;
示例:
mysql> select * from emp;
+------+--------+-----------+--------+
| id   | name   | role      | salary |
+------+--------+-----------+--------+
|    1 | 张三   | 程序员    |  10000 |
|    2 | 李四   | 程序员    |  11000 |
|    3 | 王五   | 程序员    |  12000 |
|    4 | 赵六   | 产品      |   8000 |
|    5 | 田七   | 产品      |   9000 |
|    6 | 周八   | 老板      | 100000 |
+------+--------+-----------+--------+
6 rows in set (0.00 sec)


搭配条件使用的情况:

1)分组之前的条件:查询每个岗位的平均薪资,除去张三这个人

2)分组之后的条件:查询每个岗位的平均薪资,排除平均薪资超过5w的记录

3)一个查询同时包含分组前条件和分组后条件:查询每个岗位的平均薪资,排除张三,也排除平均薪资超过5w的结果

3.1.3 having

group by 子句进行分组以后,需要对分组结果再进行条件过滤时,不能使用where语句,而需要用having,例如上面例2)和例3)。

3.2 联合查询

实际开发中往往数据来自不同的表,所以需要多表联合查询

多表查询是对多张表的数据取笛卡尔积:

tip:关联查询可以对关联表使用别名

 进行多表查询时,通常会通过一定的条件把笛卡尔积中的“合法数据”筛选出来,这样的条件就称为“连接条件”。

示例:初始化测试数据

-- 删除重复表
drop table if exists classes;
drop table if exists student;
drop table if exists course;
drop table if exists score;

-- 创建学生相关表
create table classes (id int primary key auto_increment, name varchar(20), `desc` varchar(100));

create table student (id int primary key auto_increment, sn varchar(20), name varchar(20), qq_mail varchar(20), classes_id int);

create table course (id int primary key auto_increment, name varchar(20));

create table score (score decimal(3,1), student_id int, course_id int);

-- 对各表插入相应数据
insert into classes(name, `desc`) values 
('计算机系2024级1班', '学习了计算机原理、C和Java语言、数据结构和算法'),
('中文系2024级3班','学习了中国传统文学'),
('自动化2024级5班','学习了机械自动化');


insert into student(sn, name, qq_mail, classes_id) values 
('09982','刘一','liuyi@qq.com',1), 
('00835','陈二',null,1), 
('00391','张三',null,1), 
('00031','李四','lisi@qq.com',1), 
('00054','王五',null,1), 
('51234','赵六','zhaoliu@qq.com',2), 
('83223','孙七',null,2), 
('09527','周八','zhouba@qq.com',2);

insert into course(name) values ('Java'), ('中国传统文化'),('计算机原理'),('语文'),('高等数学'),('英语');


insert into score(score, student_id, course_id) values
-- 刘一
(70.5, 1, 1),(98.5, 1, 3),(33, 1, 5),(98, 1, 6),
-- 陈二
(60, 2, 1),(59.5, 2, 5),
-- 张三
(33, 3, 1),(68, 3, 3),(99, 3, 5),
-- 李四
(67, 4, 1),(23, 4, 3),(56, 4, 5),(72, 4, 6),
-- 王五
(81, 5, 1),(37, 5, 5),
-- 赵六
(56, 6, 2),(43, 6, 4),(79, 6, 6),
-- 孙七
(80, 7, 2),(92, 7, 6);

3.2.1 内连接

语法:

select 字段 from 表1 别名1 [inner] join 表2 别名2 on 连接条件 and 其他条件;
select 字段 from 表1 别名1,表2 别名2 where 连接条件 and 其他条件;

示例:

(1) 查询“李四”同学的成绩

查询步骤:

a) 分析问题,了解清楚要查询内容在哪些表中存在(该流程使用 , where 的方式演示)

此处查询内容在 学生表 和 分数表 中

b) 对这两个表进行笛卡尔积

c) 指定连接条件,筛选掉无效数据

d) 根据需要进一步指定条件,对数据再筛选(新加的条件往往通过 and 的方式 和 连接条件放在一起)

 e) 针对查询中的列进行精简


多表查询的另一个写法 join on

a) 分析问题,了解清楚要查询内容在哪些表中存在

b) 对这两个表进行笛卡尔积

c) 指定连接条件,筛选掉无效数据(通过 on 关键字)

d) 根据需要进一步指定条件,对数据再筛选

e) 针对查询中的列进行精简

相比于直接多个表 , where 的方式来说,join on 的方式支持的功能更多一些,在实际开发中,两种写法都需掌握

(2)查询所有同学的成绩,及同学的个人信息(每个同学都有多门课程,把多门课程的分数加在一起)

tip:此处分数不是按照 来表示的,而是按照 !同时还需要搭配 分组 操作把每个同学的成绩相加

a) 分析问题,了解清楚要查询内容在哪些表中存在(学生表、分数表)

b) 对这两个表进行笛卡尔积

c) 指定连接条件,筛选掉无效数据

d) 针对上述数据进行 分组聚合


使用  join on 的方式:

(3)查询所有同学的成绩,及同学的个人信息(列出同学姓名、课程名字、课程分数)

tip:查出来的都是有成绩的同学,“周八”同学没有显示

a) 分析问题,了解清楚要查询内容在哪些表中存在

需针对 student、course、score 三个表计算笛卡尔积

b) 对这三个表进行笛卡尔积

c) 指定连接条件,筛选掉无效数据

e) 针对查询中的列进行精简


也可使用 join on 的方式来写,更能清楚的体现出联合查询的过程:

tip:虽然可以拿任意个表进行笛卡尔积操作,但是由于笛卡尔积可能会产生大量的“中间结果”,就会对性能影响很大,甚至严重的可能会将数据库搞挂,因此,多表联合查询 使用时要慎重,需要先对当前这样的多表查询大概会涉及到多少数据量操作,有一个“预估”。

3.2.2 外连接

外连接分为左外连接和右外连接,如果联合查询,左侧的表完全显示就认为是左外连接;右侧的表完全显示就认为是右外连接

语法:

-- 左外连接,表1完全显示
select 字段名  from 表名1 left join 表名2 on 连接条件;

-- 右外连接,表2完全显示
select 字段 from 表名1 right join 表名2 on  连接条件;

示例:查询所有同学的成绩,及同学的个人信息,如果该同学没有成绩,也需显示

示例数据:

, where 只能表示内连接,不能表示外连接

内连接查询结果,只包含两个表中同时具备的数据

join 前面加上 inner 就是内连接,平常可以省略不写


外连接就是给 join 前面加上 left / right 表示外连接(不支持 from 多个表)

左外连接:

左连接就是以左表为基准,能够确保左表中的每个记录都出现在最终结果中,如果左表中的记录在右表中没有对应的记录,此时就会把右表中的相关字段填为 NULL

右外连接

原理同上 左连接

全外连接:(MySQL不支持全外连接)

3.2.3 自连接

自连接是指在同一张表连接自身进行查询

示例:显示所有“计算机原理”成绩比“Java”成绩高的成绩信息

通过上面两个表,可以发现学号为 1 和学号为 3 的同学满足条件

而 sql 中进行的条件查询是针对两个 进行比较的,不能比较两个

自连接本质上就能把 行关系 转换成 列关系(典型的数学思维:将未知问题转成已知问题)

将成绩表直接笛卡尔积:

错误信息:表的别名不唯一

这里需要给这个表前后分别起不同的别名,再进行连接

此时需要指定连接条件,筛选掉无效数据(此处连接条件不能选则课程id,因为课程id是问题条件,也不能选成绩,若碰巧遇到相同的成绩就会有误差,只能选学生id作为连接条件)

 

 进一步条件筛选

上面结果说明,score表中,只有三个同学同时具有 计算机原理 和 Java 课程成绩

再进一步筛选,发现当前要查询的条件是针对 两行,而不是 两列,就可以考虑使用自连接进行转换

 自连接的时候,表非常大,连接的开销也会非常庞大,容易把数据库搞挂!

3.2.3 子查询

子查询是指嵌入在其他 sql 语句中的 select 语句,也叫嵌套查询

简单理解就是套娃,本来就一个需求,需要通过多个 sql 来完成,但现在偏要把多个 sql 合并成一个,这种做法是和软件开发的基本原则(化繁为简)背道而驰的,所以不推荐使用

单行子查询:返回一行记录的子查询

查询“王五”同学的同班同学

可以通过以下两步完成该任务

使用自连接的方式

 

很明显,这样的代码看起来更加复杂,可读性很差


多行子查询:返回多行记录的子查询

示例:查询“语文”或“英语”课程的成绩信息

1. [NOT]IN 关键字

此时不能使用 = > < 这样的运算符直接比较了

可以使用in

2. [NOT]EXISTS 关键字

exists 非常消耗时间,背后会触发大量的硬盘 IO 操作,更不推荐使用!


3.2.5 合并查询

在实际应用中,为了合并多个 select 的执行结果,可以使用结合操作符 union、union all

使用 union 和 union all 时,前后查询的结果集中,字段需要一致

1. union

该操作符用于取得两个结果集的并集,当使用该操作符时,会自动去掉结果集中的重复行

示例:查询 id 小于 3,或者名字为“英语”的课程

2. union all

该操作符用于取得两个结果集的并集,当使用该操作符时,不会去掉结果集中的重复行

示例:查询 id 小于 3,或者名字为 “Java” 的课程

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值