news 2026/9/13 17:56:59

MySQL 联合查询

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL 联合查询

联合查询是工作中用的最多的查询,而且面试的时候也非常爱考,因为SQL没啥考的难点,联合查询在SQL中稍微复杂。

一、联合查询的简单理解

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

这个结果集才是我们想要的

笛卡尔积现象:当两张表进行连接查询时,没有任何条件进行限制,最终查询结果条数是:两张表记录的乘积。

怎么避免笛卡尔积现象?添加连接条件,过滤。

联合查询时MYSQL是如何执行的?

1.取多张表的笛卡尔积

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

  1. 先从第一张表中取一条记录,然后再与第二张表的第条记录进行组合,生成一条新的记录
  2. 先从第一张表中取一条记录,然后再与第二张表的第条记录进行组合,生成一条新的记录
  3. ……
  4. 最后得到的结果就是一个全排列结果集
mysql> select * from student,class; +----+---------+----------+----+---------+ | id | name | class_id | id | name | +----+---------+----------+----+---------+ | 3 | 张三 | 1 | 2 | java112 | | 3 | 张三 | 1 | 1 | java113 | | 4 | 李四 | 1 | 2 | java112 | | 4 | 李四 | 1 | 1 | java113 | | 5 | 王五 | 2 | 2 | java112 | | 5 | 王五 | 2 | 1 | java113 | | 7 | 钱七 | 2 | 2 | java112 | | 7 | 钱七 | 2 | 1 | java113 | | 9 | 钱七1 | 2 | 2 | java112 | | 9 | 钱七1 | 2 | 1 | java113 | +----+---------+----------+----+---------+ 10 rows in set (0.00 sec)

通过观察,两张表取笛卡尔积之后,有些数据是无效数据

如何过滤掉这些无效数据?

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

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

mysql> select * from student,class where student.class_id=class.id; +----+---------+----------+----+---------+ | id | name | class_id | id | name | +----+---------+----------+----+---------+ | 3 | 张三 | 1 | 1 | java113 | | 4 | 李四 | 1 | 1 | java113 | | 5 | 王五 | 2 | 2 | java112 | | 7 | 钱七 | 2 | 2 | java112 | | 9 | 钱七1 | 2 | 2 | java112 | +----+---------+----------+----+---------+ 5 rows in set (0.00 sec)

可以通过表名.列名的方式来解决这个问题

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

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

mysql> select student.id,student.name,class.name from student,class where student.class_id=class.id; +----+---------+---------+ | id | name | name | +----+---------+---------+ | 3 | 张三 | java113 | | 4 | 李四 | java113 | | 5 | 王五 | java112 | | 7 | 钱七 | java112 | | 9 | 钱七1 | java112 | +----+---------+---------+ 5 rows in set (0.00 sec)

通过给表名起别名的方式来简化SQL语句

mysql> select s.id,s.name,c.name from student s,class c where s.class_id=c.id; +----+---------+---------+ | id | name | name | +----+---------+---------+ | 3 | 张三 | java113 | | 4 | 李四 | java113 | | 5 | 王五 | java112 | | 7 | 钱七 | java112 | | 9 | 钱七1 | java112 | +----+---------+---------+ 5 rows in set (0.00 sec)

联合查询也叫表连接查询

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

二、内连接

内连接:符合条件的数据都查询出来,这个结果就是内连接的结果。

(一)语法

1select字段from1别名1,2别名2where连接条件and其他条件;
2select字段from1别名1[inner]join2别名2on连接条件where其他条件;
mysql> select s.id,s.name,c.name from student s inner join class c on s.class_id=c.id; +----+---------+---------+ | id | name | name | +----+---------+---------+ | 3 | 张三 | java113 | | 4 | 李四 | java113 | | 5 | 王五 | java112 | | 7 | 钱七 | java112 | | 9 | 钱七1 | java112 | +----+---------+---------+ 5 rows in set (0.00 sec)

inner 可以省略,join两边是参与查询的表,on后面跟的是连接条件

mysql> select s.id,s.name,c.name from student s join class c on s.class_id=c.id; +----+---------+---------+ | id | name | name | +----+---------+---------+ | 3 | 张三 | java113 | | 4 | 李四 | java113 | | 5 | 王五 | java112 | | 7 | 钱七 | java112 | | 9 | 钱七1 | java112 | +----+---------+---------+ 5 rows in set (0.00 sec)

(二)示例

  1. 首先确定那几张表要参与查询
  2. 根据表与表之间的主外键关系,确定过滤条件
  3. 精减查询字段,得到想要的结果
  • 查询“唐三藏”同学的成绩;
mysql> select s.name, sc.score from student s join score sc on sc.student_id = s.id where s.name = '唐三藏'; +-----------+-------+ | name | score | +-----------+-------+ | 唐三藏 | 70.5 | | 唐三藏 | 98.5 | | 唐三藏 | 33 | | 唐三藏 | 98 | +-----------+-------+ 4 rows in set (0.00 sec)
  • 查询所有同学的总成绩及同学的个⼈信息
当一条SQL语句中有group by子句时,select后边只能跟两种东西,一种是参加分组的字段以及分组函数
mysql> select s.name, sum(sc.score) from student s, score sc where sc.student_id = s.id group by (s.id); +-----------+---------------+ | name | sum(sc.score) | +-----------+---------------+ | 唐三藏 | 300 | | 孙悟空 | 119.5 | | 猪悟能 | 200 | | 沙悟净 | 218 | | 宋江 | 118 | | 武松 | 178 | | 李逹 | 172 | +-----------+---------------+ 7 rows in set (0.00 sec)

联合查询步骤细化之后

  1. 确定查询中涉及到哪些表,也就是说要查询的数据保存在哪些表中
  2. 对目标表取笛卡尔积
  3. 确定连接条件
  4. 确定对整个结果集的过滤条件
  5. 精减查询字段

三、外连接

外连接:除了将符合条件的数据都查询出来之外,还要无条件的将其中一张表的所有的都展现出来,叫做外连接。

外连接的查询结果条数 >=内连接的查询结果条数

在外连接的时候,如果对方表没有与之匹配的数据,则自动模拟NULL匹配。

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

mysql> select * from class; +----+---------+ | id | name | +----+---------+ | 1 | java113 | | 2 | java112 | | 3 | java114 | +----+---------+ 3 rows in set (0.00 sec) mysql> select * from student; +----+---------+----------+ | id | name | class_id | +----+---------+----------+ | 3 | 张三 | 1 | | 4 | 李四 | 1 | | 5 | 王五 | 2 | | 7 | 钱七 | 2 | | 9 | 钱七1 | 2 | +----+---------+----------+ 5 rows in set (0.00 sec)

当前学生表中的记录,并没有一个学生的班级是java114

mysql> select * from student,class where student.class_id=class.id; +----+---------+----------+----+---------+ | id | name | class_id | id | name | +----+---------+----------+----+---------+ | 3 | 张三 | 1 | 1 | java113 | | 4 | 李四 | 1 | 1 | java113 | | 5 | 王五 | 2 | 2 | java112 | | 7 | 钱七 | 2 | 2 | java112 | | 9 | 钱七1 | 2 | 2 | java112 | +----+---------+----------+----+---------+ 5 rows in set (0.00 sec)

使用内连接时并没有java114班的数据

(一)语法

--左外连接,表1完全显⽰
select字段名from表名1leftjoin表名2on连接条件;
--右外连接,表2完全显⽰
select字段from表名1rightjoin表名2on连接条件;
mysql> select * from student right join class on student.class_id=class.id; +------+---------+----------+----+---------+ | id | name | class_id | id | name | +------+---------+----------+----+---------+ | 4 | 李四 | 1 | 1 | java113 | | 3 | 张三 | 1 | 1 | java113 | | 9 | 钱七1 | 2 | 2 | java112 | | 7 | 钱七 | 2 | 2 | java112 | | 5 | 王五 | 2 | 2 | java112 | | NULL | NULL | NULL | 3 | java114 | +------+---------+----------+----+---------+ 6 rows in set (0.00 sec)
没有学生是java114班的记录
java114 右表真实存在的记录

class:右连接是以join右边的表为基准,这个表中的数据会全部显示出来,左边的表没有与之匹配的记录全部分NULL去填充。

(二)示例

  • 查询没有参加考试的同学信息
  1. 在同学表中有记录
  2. 在分数表中没有该同学对应的记录
# 左连接以JOIN左边的表为基准,左表显⽰全部记录,右表中没有匹配的记录⽤NULL填充 mysql> select s.id, s.name, s.sno, s.age, sc.* from student s LEFT JOIN score sc on sc.student_id = s.id; +----+--------------+--------+------+------+-------+------------+-----------+ | id | name | sno | age | id | score | student_id | course_id | +----+--------------+--------+------+------+-------+------------+-----------+ | 1 | 唐三藏 | 100001 | 18 | 1 | 70.5 | 1 | 1 | | 1 | 唐三藏 | 100001 | 18 | 2 | 98.5 | 1 | 3 | | 1 | 唐三藏 | 100001 | 18 | 3 | 33 | 1 | 5 | | 1 | 唐三藏 | 100001 | 18 | 4 | 98 | 1 | 6 | | 2 | 孙悟空 | 100002 | 18 | 5 | 60 | 2 | 1 | | 2 | 孙悟空 | 100002 | 18 | 6 | 59.5 | 2 | 5 | | 3 | 猪悟能 | 100003 | 18 | 7 | 33 | 3 | 1 | | 3 | 猪悟能 | 100003 | 18 | 8 | 68 | 3 | 3 | | 3 | 猪悟能 | 100003 | 18 | 9 | 99 | 3 | 5 | | 4 | 沙悟净 | 100004 | 18 | 10 | 67 | 4 | 1 | | 4 | 沙悟净 | 100004 | 18 | 11 | 23 | 4 | 3 | | 4 | 沙悟净 | 100004 | 18 | 12 | 56 | 4 | 5 | | 4 | 沙悟净 | 100004 | 18 | 13 | 72 | 4 | 6 | | 5 | 宋江 | 200001 | 18 | 14 | 81 | 5 | 1 | | 5 | 宋江 | 200001 | 18 | 15 | 37 | 5 | 5 | | 6 | 武松 | 200002 | 18 | 16 | 56 | 6 | 2 | | 6 | 武松 | 200002 | 18 | 17 | 43 | 6 | 4 | | 6 | 武松 | 200002 | 18 | 18 | 79 | 6 | 6 | | 7 | 李逹 | 200003 | 18 | 19 | 80 | 7 | 2 | | 7 | 李逹 | 200003 | 18 | 20 | 92 | 7 | 6 | | 8 | 不想毕业 | 200004 | 18 | NULL | NULL | NULL | NULL | +----+--------------+--------+------+------+-------+------------+-----------+ 21 rows in set (0.00 sec) # 过滤参加了考试的同学 mysql> select s.* from student s LEFT JOIN score sc on sc.student_id = s.id where sc.score is null; +----+--------------+--------+------+--------+-------------+----------+ | id | name | sno | age | gender | enroll_date | class_id | +----+--------------+--------+------+--------+-------------+----------+ | 8 | 不想毕业 | 200004 | 18 | 1 | 2000-09-01 | 1 | +----+--------------+--------+------+--------+-------------+----------+ 1 row in set (0.00 sec)

四、自连接

(一)应用场景

自连接:一张表看作两张表,自己与自己进行表连接

可以把行转化为列,再查询的时候可以使用where条件进行过滤,也就是说可以实现行与行之间的比较功能

mysql> select * from exam; +------+--------------+---------+------+---------+ | id | name | chinese | math | english | +------+--------------+---------+------+---------+ | 1 | 唐三藏 | 67.0 | 98.0 | 56.0 | | 3 | 猪悟能 | 88.0 | 98.0 | 90.0 | | 4 | 曹孟德 | 70.0 | 60.0 | 67.0 | | 5 | 刘玄德 | 55.5 | 85.0 | 45.0 | | 6 | 孙权 | 70.0 | 73.0 | 78.5 | | 7 | 宋公明 | 75.0 | 65.0 | 30.0 | | 8 | 孙行者 | 87.5 | 78.0 | 77.0 | | 13 | 赵云 | 40.0 | 80.0 | 90.0 | | 14 | 黄忠 | 60.0 | 70.0 | 50.0 | | NULL | 测试用户 | 90.0 | 80.0 | NULL | +------+--------------+---------+------+---------+ 10 rows in set (0.00 sec)

这样的表设计,可以在一行中进行列与列之间的比较

(二)示例

  • 案例:找出每个员工的直属领导,要求显示员工名、领导名。
  1. 确定涉及的表 员工表,领导表
  2. 取笛卡尔积
select e.ename 员工名, l.ename 领导名 from emp e join emp l on e.mgr = l.empno;

思路:
将emp表当做员工表 e

将emp表当做领导表 l

五、子查询

子查询是把一条SQL的查询结果,当做另一条SQL的查询条件,可以嵌套很多很多层,也叫嵌套查询。

子查询可以出现在哪里?

select...(select)

from...(select)

where...(select)

(一)语法

select*fromtable1wherecol_name1 {= |IN} (
selectcol_name1fromtable2wherecol_name2 {= |IN} [(
select...)
] ...
)

可以看出子查询是由很多条SQL语句组成的,也可以把子查询分成一条一条单独的语句去执行,最后再把结果和条件拼接在一起,得到查询结果。

(二)单行子查询

  • ⽰例:查询与"不想毕业"同学的同班同学
mysql> select * from student where class_id = (select class_id from student where name = '不想毕业'); +----+--------------+--------+------+--------+-------------+----------+ | id | name | sno | age | gender | enroll_date | class_id | +----+--------------+--------+------+--------+-------------+----------+ | 5 | 宋江 | 200001 | 18 | 1 | 2000-09-01 | 2 | | 6 | 武松 | 200002 | 18 | 1 | 2000-09-01 | 2 | | 7 | 李逹 | 200003 | 18 | 1 | 2000-09-01 | 2 | | 8 | 不想毕业 | 200004 | 18 | 1 | 2000-09-01 | 2 | +----+--------------+--------+------+--------+-------------+----------+ 4 rows in set (0.00 sec)

(三)多行子查询

返回多行记录的子查询~~返回的是一个集合,集合包含多个对象

select * from table1 where table1.id IN (select id from table2 where xxx=...);

  • 示例:查询"MySQL"或"Java"课程的成绩信息
mysql> select * from score where course_id in (select id from course where name = 'Java' or name = 'MySQL'); +----+-------+------------+-----------+ | id | score | student_id | course_id | +----+-------+------------+-----------+ | 1 | 70.5 | 1 | 1 | | 5 | 60 | 2 | 1 | | 7 | 33 | 3 | 1 | | 10 | 67 | 4 | 1 | | 14 | 81 | 5 | 1 | | 2 | 98.5 | 1 | 3 | | 8 | 68 | 3 | 3 | | 11 | 23 | 4 | 3 | +----+-------+------------+-----------+ 8 rows in set (0.00 sec)

(四)在from子句中使用子查询

将查询结果当做一张临时表

  • 案例:找出每个部门的平均工资的等级。
  1. 找出每个部门的平均工资
select deptno, avg(sal) avgsal from emp group by deptno;

2.将以上查询结果当做临时表t,t表和salgrade表进行连接查询。条件:t.avgsal between s.losal and s.hisal

select t.*,s.grade from (select deptno, avg(sal) avgsal from emp group by deptno) t join salgrade s on t.avgsal between s.losal and s.hisal;

(五)exists、not exists

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

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

内层查询返回非空结果集

mysql> select * from student where id=3; +----+--------+----------+ | id | name | class_id | +----+--------+----------+ | 3 | 张三 | 1 | +----+--------+----------+ 1 row in set (0.00 sec)
mysql> select * from student where exists ( select * from student where id=3); +----+---------+----------+ | id | name | class_id | +----+---------+----------+ | 3 | 张三 | 1 | | 4 | 李四 | 1 | | 5 | 王五 | 2 | | 7 | 钱七 | 2 | | 9 | 钱七1 | 2 | +----+---------+----------+ 5 rows in set (0.00 sec)

exists相当于if语句的判断条件,有结果返回true,没有返回false

内层查询返回空结果集,外层查询也返回空结果集,也可以说外层查询没有执行

mysql> select * from student where id=10; Empty set (0.00 sec) mysql> select * from student where exists ( select * from student where id=10); Empty set (0.00 sec)
mysql> select NUll; +------+ | NULL | +------+ | NULL | +------+ 1 row in set (0.00 sec) mysql> select * from student where exists ( select null); +----+---------+----------+ | id | name | class_id | +----+---------+----------+ | 3 | 张三 | 1 | | 4 | 李四 | 1 | | 5 | 王五 | 2 | | 7 | 钱七 | 2 | | 9 | 钱七1 | 2 | +----+---------+----------+ 5 rows in set (0.00 sec)

返回的结果集是一个非空的,只不过列名为null,值也为null而已

六、合并查询

union和unionall

不管是union还是union all都可以将两个查询结果集进行合并。
union会对合并之后的查询结果集进行去重操作。
union all是直接将查询结果集合并,不进行去重操作。(union all和union都可以完成的话,优先选择union all,union all因为不需要去重,所以效率高一些。)

  • 案例:查询工作岗位是MANAGER和SALESMAN的员工
select ename,sal from emp where job='MANAGER' union all select ename,sal from emp where job='SALESMAN';

以上案例采用 in 也可以完成,那 in 和union all有什么区别?
考虑走索引优化之类的选择union all,其它选择 in(in 使用不当很容易导致索引失效)。【in 比 or 效率高】
两个结果集合并时,列数量要相同。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 17:53:14

PDFPatcher PDF 工具箱新手指南

PDFPatcher PDF 工具箱新手指南 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱,可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档,探查文档结构,提取图片、转成图片等等 项目地址: https://gitcode.com/GitHub_Trending/pd/PDF…

作者头像 李华
网站建设 2026/9/13 17:52:55

解决Node.js连接MySQL 8.0认证协议不兼容问题

1. 问题现象与背景分析最近在本地开发环境搭建Node.js后端服务时,遇到了一个典型的数据库连接问题。当我尝试用mysql2包连接新安装的MySQL 8.0数据库时,控制台抛出了如下错误:ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentic…

作者头像 李华
网站建设 2026/9/13 17:52:28

15个Verilog文件造出一颗GPU:tiny-gpu极简并行架构拆解

15个Verilog文件造出一颗GPU:tiny-gpu极简并行架构拆解 【免费下载链接】tiny-gpu A minimal GPU design in Verilog to learn how GPUs work from the ground up 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny-gpu 如果你只能给一颗GPU写11条指令…

作者头像 李华