当前位置: 首页 > news >正文

MySQL联合查询详解

联合查询

设计数据表是把表进行拆分,为了消除表中的字段依赖关系(部分函数依赖,传递依赖…),这时会导致一条SQL查出来的数据,对于业务来说是不完整的,我们就可以使用联合查询把关系中的数据全部查出来,在一个数据行中显示详细信息

联合查询时MySQL是如何执行的

1.取多张表的笛卡尔积

表格一student

编号姓名性别班级编号
1张三1
2李四1
3王五2
4赵六2

表格二class

编号班级
11班
22班

对多张表进行笛卡尔积的过程,

  1. 先从第一张表中取一条记录,然后再与第二张表中的第一条记录进行组合,生成一条新的记录

  2. 先从第一张表中取一条记录,然后再与第二张表中的第二条记录进行组合,生成一条新的记录 ……

    最后得到的结果就是一个全排列结果集

编号姓名性别编号班级
1张三11班
1张三22班
2李四11班
2李四22班
3王五11班
3王五22班
4赵六11班
4赵六22班

语法:

select * from 表名,表名;

示例:

select * from student,class;

在这里插入图片描述

从查询的结果可以看见,两张表取笛卡尔积之后,有些数据是无效数据

2.通过连接条件过滤掉无效数据

两个表之间是有主外键关系,只需要判断两个表中的主外键字段是否相等即可

select * from student,class where student.class_id = class.class_id;

在这里插入图片描述

**注意:**不能写成select * from student,class where class_id = class_id;,因为class_id在两张表中都存在,MySQL分不清当前语句中的class_id应该取自哪张表。可以通过表名.列名的方式来解决这个问题。

3.通过指定列查询,精简结果集

查询列表中通过表名.列名的方式指定要查询的字段

select student.id,student.name,class.name from student,class where student.class_id=class.class_id;

可以通过给表取别名的方式来精简代码

select s.id,s.name,c.name from student s,class c where s.class_id=c.class_id;

联合查询也叫表连接查询:

  1. 首先确定哪几张表要参与查询
  2. 根据表与表之间的主外键关系 确定过滤条件
  3. 精简查询字段,得到想要的结果

示例用表

创建表:

-- 课程表
CREATE TABLE course (id INT PRIMARY KEY AUTO_INCREMENT COMMENT '课程ID',name VARCHAR(50) NOT NULL COMMENT '课程名称'
)-- 班级表
CREATE TABLE class (id INT PRIMARY KEY AUTO_INCREMENT COMMENT '班级ID',name VARCHAR(50) NOT NULL COMMENT '班级名称'
)-- 学生表
CREATE TABLE student (id INT PRIMARY KEY AUTO_INCREMENT COMMENT '学生ID',name VARCHAR(20) NOT NULL COMMENT '姓名',sno VARCHAR(20) NOT NULL COMMENT '学号',age TINYINT UNSIGNED NOT NULL COMMENT '年龄',gender TINYINT COMMENT '性别(1男,0女)',enroll_date DATE COMMENT '入学日期',class_id INT COMMENT '班级ID',CONSTRAINT fk_student_class FOREIGN KEY (class_id) REFERENCES class (id)
)-- 成绩表
CREATE TABLE score (id INT PRIMARY KEY AUTO_INCREMENT COMMENT '成绩记录ID',score DECIMAL(5,2) COMMENT '成绩',student_id INT NOT NULL COMMENT '学生ID',course_id INT NOT NULL COMMENT '课程ID',CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student (id),CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course (id)
)

插入数据:

# 课程表
insert into course (name) values ('Java'), ('C++'), ('MySQL'), ('操作系统'), ('计算机⽹络'), ('数据结构');
# 班级表
insert into class(name) values ('Java001班'), ('C++001班'), ('前端001班');
# 学⽣表
insert into student (name, sno, age, gender, enroll_date, class_id) values
('唐三藏', '100001', 18, 1, '1986-09-01', 1),
('孙悟空', '100002', 18, 1, '1986-09-01', 1),
('猪悟能', '100003', 18, 1, '1986-09-01', 1),
('沙悟净', '100004', 18, 1, '1986-09-01', 1),
('宋江', '200001', 18, 1, '2000-09-01', 2),
('武松', '200002', 18, 1, '2000-09-01', 2),
('李逹', '200003', 18, 1, '2000-09-01', 2),
('不想毕业', '200004', 18, 1, '2000-09-01', 2);
# 成绩表
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);

内连接

上面的示例便是一个内连接

语法:

-- 写法一:
select 字段 from1 别名1,2 别名2 where 连接条件 and 其他条件;
-- 写法二:
select 字段 from1 别名1 [inner] join2 别名2 on 连接条件 where 其他条件;

写法二中的inner是可选项,也就是说可以省略

示例:

select s.id,s.name,c.name from student s,class c where s.class_id=c.id;select s.id,s.name,c.name from student s inner join class c on s.class_id=c.id;select s.id,s.name,c.name from student s join class c on s.class_id=c.id;

联合查询详细步骤

  1. 确定查询中涉及到哪些表
  2. 对目标表取笛卡尔积
  3. 确定连接条件
  4. 确定对整个结果集的过滤条件
  5. 精简查询字段

内连接实例:

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

SELECT s.name AS 学生姓名,c.name AS 课程名,sc.score AS 成绩
FROM student s,score sc,course c
WHERE s.id = sc.student_idAND sc.course_id = c.idAND s.name = '李逹';

或者

SELECT s.name AS 学生姓名,c.name AS 课程名,sc.score AS 成绩
FROM student s
JOIN score sc ON s.id = sc.student_id
JOIN course c ON sc.course_id = c.id
WHERE s.name = '李逹';

(2)查询所有同学的总成绩及个人信息

select st.id,st.name,SUM(sc.score) as 总分 	
from student st,score sc,course c 
where  st.id = sc.student_id AND sc.course_id = c.id 
group by sc.student_id;

外连接

  • 外连接分为左外连接右外连接全外连接三种类型,MySQL不⽀持全外连接。
  • **左外连接:**返回左表的所有记录和右表中匹配的记录。如果右表中没有匹配的记录,则结果集中对 应字段会显示为NULL。
  • **右外连接:**与左外连接相反,返回右表的所有记录和左表中匹配的记录。如果左表中没有匹配的记 录,则结果集中对应字段会显示为NULL。
  • **全外连接:**结合了左外连接和右外连接的特点,返回左右表中的所有记录。如果某⼀边表中没有匹 配的记录,则结果集中对应字段会显示为NULL。

语法:

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

示例:

(1)(左连接)查询没有参加考试的同学信息

在学生表中插入的('不想毕业', '200004', 18, 1, '2000-09-01', 2)这条数据并没有出现在score表中,使用内连接就无法查出这条数据。而使用外连接就可以查询。

# 左连接以JOIN左边的表为基准,左表显⽰全部记录,右表中没有匹配的记录⽤NULL填充
SELECT s.id, s.name, s.sno, s.age, sc.*
FROM student s
LEFT JOIN score sc ON sc.student_id = s.id;
s.ids.names.snos.agesc.idsc.scoresc.student_idsc.course_id
1唐三藏10000118170.511
1唐三藏10000118298.513
1唐三藏1000011833315
1唐三藏1000011849816
2孙悟空1000021856021
2孙悟空10000218659.525
3猪悟能1000031873331
3猪悟能1000031886833
3猪悟能1000031899935
4沙悟净10000418106741
4沙悟净10000418112343
4沙悟净10000418125645
4沙悟净10000418137246
5宋江20000118148151
5宋江20000118153755
6武松20000218165662
6武松20000218174364
6武松20000218187966
7李逹20000318198072
7李逹20000318209276
8不想毕业20000418NULLNULLNULLNULL

(2)(左连接)查询没有参加考试的同学(不在score表中,但在student表的数据)

-- 内连接
SELECT s.* 
FROM student s 
JOIN score sc ON sc.student_id = s.id 
WHERE sc.score IS NULL;-- 外连接(左连接)
SELECT s.* 
FROM student s 
LEFT JOIN score sc ON sc.student_id = s.id 
WHERE sc.score IS NULL;
特性LEFT JOIN 版本INNER JOIN 版本
查找的是没有任何成绩记录的学生有成绩记录但成绩为 NULL 的学生
学生必须在 score 表有记录吗?不需要必须有
结果包含student 表中所有未出现在 score 表的学生score 表中成绩字段为 NULL 的学生

(3)(右连接)查询没有学⽣的班级

# 右连接以JOIN右边的表为基准,右表显⽰全部记录,左表中没有匹配的记录⽤NULL填充
select * from student s RIGHT JOIN class c on c.id = s.class_id;
s.ids.names.snos.ages.genders.enroll_dates.class_idc.idc.name
1唐三藏1000011811986-09-0111Java001 班
2孙悟空1000021811986-09-0111Java001 班
3猪悟能1000031811986-09-0111Java001 班
4沙悟净1000041811986-09-0111Java001 班
5宋江2000011812000-09-0122C++001 班
6武松2000021812000-09-0122C++001 班
7李逹2000031812000-09-0122C++001 班
8不想毕业2000041812000-09-0122C++001 班
NULLNULLNULLNULLNULLNULLNULL3前端 001 班

自连接

应用场景

​ ⾃连接是⾃⼰与⾃⼰取笛卡尔积,可以把⾏转化成列,在查询的时候可以使⽤where条件对结果进⾏过滤,或者说实现⾏与⾏之间的⽐较。在做表连接时为表起不同的别名。

# 不为表指定别名
mysql> select * from score, score;
ERROR 1066 (42000): Not unique table/alias: 'score'
# 指定别名
mysql> select * from score s1, score s2;

在这里插入图片描述

示例:显示所有的“Java”成绩比“MySQL”成绩高的同学

左表这样的表设计,可以在一行中通过列与列的比较而得到查询结果,但是不便于维护和修改数据。

右表的表设计满足第⼀范式(1NF)、第⼆范式(2NF)、第三范式(3NF)但是无法做到行与行之间的比较

此时就能通过自连接完成需求。

分两步:

mysql> select * from course where name = 'Java' or name = 'MySQL';
+----+-------+
| id | name |
+----+-------+
| 1 | Java |
| 3 | MySQL |
+----+-------+
2 rows in set (0.00 sec)
select * from score s1, score s2 
where s1.student_id=s2.student_id 
and s1.course_id=1 
and s2.course_id=3 
and s1.score>s2.score;

结合在一起

select s1.* fromscore s1, score s2, course c1,course c2
wheres1.student_id = s2.student_id
ands1.course_id = c1.id
ands2.course_id = c2.id 
ands1.score > s2.score
andc1.`name` = 'MySQL'
andc2.`name` = 'Java';+----+-------+------------+-----------+
| id | score | student_id | course_id |
+----+-------+------------+-----------+
| 2  |  98.5 |       1    |    3      |
| 8  |  68   |       3    |    3      |
+----+-------+------------+-----------+

练习:

显⽰所有"MySQL"成绩⽐"Java"成绩⾼的学⽣信息和班级以及成绩信息

select  stu.name as 姓名, c.name as 班级, s1.score as MySQL分数, s2.score as Java分数 
from  student stu,score s1,score s2,course c1,course c2,class c
wheres1.student_id = s2.student_idand c1.id=s1.course_idand c2.id=s2.course_idand stu.id=s1.student_idand stu.class_id = c.idand c1.name="MySQL"and c2.name="Java"and s1.score>s2.score;

结果集

姓名班级MySQL 分数Java 分数
唐三藏Java001 班98.570.5
猪悟能Java001 班6833

子查询

子查询是把⼀个SELECT语句的结果当做别⼀个SELECT语句的条件,也叫嵌套查询

可以嵌套很多层

在这里插入图片描述

单行子查询

嵌套的查询中只返回一行数据

示例:查询与"不想毕业"同学的同班同学

select * from student where class_id=(select class_id from student where name='不想毕业');
idnamesnoagegenderenroll_dateclass_id
5宋江2000011812000-09-012
6武松2000021812000-09-012
7李逹2000031812000-09-012
8不想毕业2000041812000-09-012

多行子查询

嵌套的查询中返回多⾏数据,使⽤[NOT] IN关键字

示例:查询"MySQL"或"Java"课程的成绩信息

select * from score where course_id in (select id from course where name = 'Java' or name = 'MySQL');

使⽤NOT IN 可以查询除了"MySQL"或"Java"课程的成绩

select * from score where course_id not in (select id from course where name = 'Java' or name = 'MySQL');

多列子查询

单行子查询和多行子查询都只返回⼀列数据,多列⼦查询中可以返回多个列的数据,外层查询与嵌套的内层查询的列要匹配

**⽰例:**查询重复录⼊的分数

# 插⼊重复的分数:score, student_id, course_id列重复
insert into score(score, student_id, course_id) values (70.5, 1, 1),(98.5, 1, 3),(60, 2, 1);# ⼦查询中返回多个列
SELECT * FROM score WHERE (score, student_id, course_id )IN (SELECT score, student_id,course_id FROM score GROUP BY score, student_id, course_id HAVING count( 0 ) > 1);

在from子句中使用子查询

当⼀个查询产⽣结果时,MySQL⾃动创建⼀个临时表,然后把结果集放在这个临时表中,最终返回 给⽤⼾,在from⼦句中也可以使⽤临时表进⾏⼦查询或表连接操作

**⽰例:**查询所有⽐"Java001班"平均分⾼的成绩信息

#⾸先分步进⾏,第⼀步先查出Java001班的平均分
mysql> select avg(sc.score) score from student s join class c on s.class_id = c.id join score sc on s.id = sc.student_idwherec.name = 'Java001班';# 把以上查询做为临时表,与真实表进⾏⽐较
select * from score s, (select avg(sc.score) score from student s join class c on s.class_id = c.id join score sc on s.id = sc.student_idwherec.name = 'Java001班') tmp where s.score > tmp.score;

[NOT] EXISTS关键字

语法:select * from 表名 where [not] exists (select * from 表名1);

exists 后面括号中查询的语句,如果有结果的话,则执行外层的查询;如果返回的是一个空结果集,则不执行外层的查询。

**注意:**如果exists后面的括号的语句是select NULL;它也会执行外层的查询。因为类似于这要的查询语句他返回的结果集不为空,只不过列名为NULL,值也为NULL

在这里插入图片描述

合并查询

合并多个查询结果到一个结果集中,可以使用集合操作符unino,union all

创建示例用表

create table student1 like student;insert into student1 (name, sno, age, gender, enroll_date, class_id) values
('唐三藏', '100001', 18, 1, '1986-09-01', 1),
('刘备', '300001', 18, 1, '1993-09-01', 3),
('张⻜', '300002', 18, 1, '1993-09-01', 3),
('关⽻', '300003', 18, 1, '1993-09-01', 3);

注意:合并时两个SQL的查询字段要具有相同意义并且按照顺序一一对应

Union

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

**⽰例:**查询student表中 id < 3 的同学和student1表中的所有同学

# 结果集中有两张表中的数据,但是唐三藏是重复数据,所以只返回了⼀条记录
select *from student where id<3 union select * from student1;

结果集:

idnamesnoagegenderenroll_dateclass_id
1唐三藏1000011811986-09-011
2孙悟空1000021811986-09-011
2刘备3000011811993-09-013
3张⻜3000021811993-09-013
4关⽻3000031811993-09-013

Union all

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

# 结果集中有两张表中的数据,返回了所有唐三藏的记录
select * from student where id < 3 union all select * from student1;
idnamesnoagegenderenroll_dateclass_id
1唐三藏1000011811986-09-011
2孙悟空1000021811986-09-011
1唐三藏1000011811986-09-011
2刘备3000011811993-09-013
3张飞3000021811993-09-013
4关羽3000031811993-09-013

插入查询结果

语法

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

⽰例

将student表中C++001班的学⽣复制到student1表中

insert into student1 (name, sno, age, gender, enroll_date, class_id) select s.name, s.sno, s.age, s.gender, s.enroll_date, s.class_idfrom student s, class c where s.class_id = c.id and c.name = 'C++001班';
http://www.dtcms.com/a/528156.html

相关文章:

  • webrtc代码走读(五)-QOS-FEC原理
  • 车载诊断架构 ---DTC快照中DID大小顺序是怎么要求的?
  • Windows 10 下 VS Code 配置 C++ 开发环境(MinGW)
  • 天津低价网站建设怎样做淘宝联盟的网站
  • 福建网站建建设方案太原关键词优化报价
  • 深耕 Rust:核心技术解析、生态实践与高性能开发指南
  • 深入浅出 Tokio 源码:掌握 Rust 异步编程的底层逻辑
  • 北京网站建设管庄1天学会搭建营销网站
  • 基于SEH的异常捕获与MiniDumpWriteDump深度解析
  • C语言练习题
  • Postman应用实战
  • Vue-Loader 深度解析:原理、使用与最佳实践
  • HCIP第二次作业(VRRP/STP/VLAN/Eth-trunk/NAT)
  • 外国设计网站推荐自己学网站建设
  • ASP.NET Core中创建中间件的几种方式
  • Docker安装思源笔记使用指南
  • 需求登记网站怎么做免费高清图片素材网站推荐
  • SpringBoot集成Elasticsearch | Java High Level Rest Client(HLRC)方式
  • 《神领物流》day07-线路规划之线路管理_完整代码【简单易懂注释版】
  • 使用Ansys Polyflow对泡沫聚合物挤出进行建模
  • 【组成原理·硬件】6总线
  • Spring Boot3零基础教程,整合 SSM,笔记52
  • 序列化详解
  • 网站设计制作电影福建网站建设公司
  • 记录一次Oracle日志listener.log文件大小超过4G后出现Tomcat服务启动一直报错的原因【ORACLE】
  • Docker Desktop快速搭建本地k8s集群
  • LabVIEW超高分辨显微成像系统
  • 东莞建网站的公破解付费wordpress主题
  • 国产数据库破局:金仓数据库如何无缝替代MongoDB支撑2TB政务数据
  • Switch 20.5.0系统最新PSP模拟器懒人包