MySQL复合查询(8)

目录

    • [1. 基本查询回顾](#1. 基本查询回顾)
    • [2. 多表查询](#2. 多表查询)
      • [2.1 笛卡尔积](#2.1 笛卡尔积)
      • [2.2 显示雇员名、雇员工资以及所在部门的名字](#2.2 显示雇员名、雇员工资以及所在部门的名字)
      • [2.3 显示部门号为 10 的部门名、员工名和工资](#2.3 显示部门号为 10 的部门名、员工名和工资)
      • [2.4 显示各个员工的姓名、工资及工资级别](#2.4 显示各个员工的姓名、工资及工资级别)
    • [3. 自连接](#3. 自连接)
    • [4. 子查询](#4. 子查询)
      • [4.1 单行子查询](#4.1 单行子查询)
      • [4.2 多行子查询](#4.2 多行子查询)
      • [4.3 多列子查询](#4.3 多列子查询)
      • [4.4 在 FROM 子句中使用子查询](#4.4 在 FROM 子句中使用子查询)
    • [5. 合并查询](#5. 合并查询)
      • [5.1 UNION](#5.1 UNION)
      • [5.2 UNION ALL](#5.2 UNION ALL)

前面我们讲解的 MySQL 查询大多基于单张表,但在实际开发中,数据往往分散在多张表中。本文从基本查询回顾出发,逐步深入到多表查询、自连接、子查询、合并查询,配合大量案例与执行结果,带你彻底掌握 MySQL 复合查询,让复杂业务查询不再成为难题!


1. 基本查询回顾

在进入复合查询之前,先快速回顾几个常用单表查询案例。

查询工资高于 500 或岗位为 MANAGER 的雇员,同时姓名首字母为大写:

sql 复制代码
select * from EMP
where (sal > 500 or job = 'MANAGER') and ename like 'J%';

按照部门号升序、雇员工资降序排序:

sql 复制代码
select * from EMP order by deptno, sal desc;

使用年薪进行降序排序:

sql 复制代码
select ename, sal * 12 + ifnull(comm, 0) as '年薪'
from EMP
order by 年薪 desc;

显示工资最高的员工的名字和工作岗位:

sql 复制代码
select ename, job from EMP
where sal = (select max(sal) from EMP);

显示工资高于平均工资的员工信息:

sql 复制代码
select ename, sal from EMP
where sal > (select avg(sal) from EMP);

显示每个部门的平均工资和最高工资:

sql 复制代码
select deptno, format(avg(sal), 2), max(sal)
from EMP
group by deptno;

显示平均工资低于 2000 的部门号和它的平均工资:

sql 复制代码
select deptno, avg(sal) as avg_sal
from EMP
group by deptno
having avg_sal < 2000;

显示每种岗位的雇员总数、平均工资:

sql 复制代码
select job, count(*), format(avg(sal), 2)
from EMP
group by job;

这些单表查询是复合查询的基础,接下来进入多表查询。


2. 多表查询

实际开发中,数据往往来自不同的表,因此需要多表联合查询。本节以公司管理系统为例,涉及三张表:EMPDEPTSALGRADE

2.1 笛卡尔积

多表查询时,如果不加过滤条件,会得到两张表的笛卡尔积:

  1. 从第一张表中选出第一条记录,和第二张表的所有记录组合;
  2. 然后从第一张表中取第二条记录,和第二张表中的所有记录组合;
  3. 不加过滤条件,得到的结果称为笛卡尔积。

因此,多表查询必须加上正确的连接条件。

2.2 显示雇员名、雇员工资以及所在部门的名字

数据来自 EMPDEPT,需要联合查询:

sql 复制代码
select EMP.ename, EMP.sal, DEPT.dname
from EMP, DEPT
where EMP.deptno = DEPT.deptno;

2.3 显示部门号为 10 的部门名、员工名和工资

sql 复制代码
select ename, sal, dname
from EMP, DEPT
where EMP.deptno = DEPT.deptno
  and EMP.deptno = 10;

2.4 显示各个员工的姓名、工资及工资级别

sql 复制代码
select ename, sal, grade
from EMP, SALGRADE
where EMP.sal between losal and hisal;

3. 自连接

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

案例:显示员工 FORD 的上级领导的编号和姓名(mgr 是员工领导的编号,对应 empno)。

使用子查询:

sql 复制代码
select empno, ename
from emp
where emp.empno = (select mgr from emp where ename = 'FORD');

使用多表查询(自查询):

sql 复制代码
select leader.empno, leader.ename
from emp leader, emp worker
where leader.empno = worker.mgr
  and worker.ename = 'FORD';

这里给同一张表起了两个别名:leader 表示领导,worker 表示员工。因为要先做笛卡尔积,所以别名可以提前识别。


4. 子查询

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

4.1 单行子查询

返回一行记录的子查询。

显示 SMITH 同一部门的员工:

sql 复制代码
select *
from EMP
where deptno = (select deptno from EMP where ename = 'SMITH');

4.2 多行子查询

返回多行记录的子查询,常用关键字有 inallany

in 关键字:查询和 10 号部门工作岗位相同的雇员的名字、岗位、工资、部门号,但不包含 10 号部门自己:

sql 复制代码
select ename, job, sal, deptno
from emp
where job in (select distinct job from emp where deptno = 10)
  and deptno <> 10;

all 关键字:显示工资比部门 30 的所有员工工资都高的员工姓名、工资和部门号:

sql 复制代码
select ename, sal, deptno
from EMP
where sal > all(select sal from EMP where deptno = 30);

any 关键字:显示工资比部门 30 的任意员工工资高的员工姓名、工资和部门号(包含自己部门的员工):

sql 复制代码
select ename, sal, deptno
from EMP
where sal > any(select sal from EMP where deptno = 30);

4.3 多列子查询

单行子查询只返回单列单行;多行子查询返回单列多行;多列子查询则返回多个列数据。

案例:查询和 SMITH 的部门和岗位完全相同的所有雇员,不含 SMITH 本人:

sql 复制代码
select ename
from EMP
where (deptno, job) = (select deptno, job from EMP where ename = 'SMITH')
  and ename <> 'SMITH';

4.4 在 FROM 子句中使用子查询

子查询可以出现在 from 子句中,此时把一个子查询当作临时表使用。

案例 1:显示每个高于自己部门平均工资的员工姓名、部门、工资、平均工资。

sql 复制代码
select ename, deptno, sal, format(asal, 2)
from EMP,
     (select avg(sal) asal, deptno dt from EMP group by deptno) tmp
where EMP.sal > tmp.asal
  and EMP.deptno = tmp.dt;

案例 2:查找每个部门工资最高的人的姓名、工资、部门、最高工资。

sql 复制代码
select EMP.ename, EMP.sal, EMP.deptno, ms
from EMP,
     (select max(sal) ms, deptno from EMP group by deptno) tmp
where EMP.deptno = tmp.deptno
  and EMP.sal = tmp.ms;

案例 3:显示每个部门的信息(部门名、编号、地址)和人员数量。

方法 1:使用多表查询。

sql 复制代码
select DEPT.dname, DEPT.deptno, DEPT.loc, count(*) '部门人数'
from EMP, DEPT
where EMP.deptno = DEPT.deptno
group by DEPT.deptno, DEPT.dname, DEPT.loc;

方法 2:使用子查询。

sql 复制代码
-- 先对 EMP 表进行人员统计
select count(*), deptno from EMP group by deptno;

-- 将上面的表看作临时表
select DEPT.deptno, dname, mycnt, loc
from DEPT,
     (select count(*) mycnt, deptno from EMP group by deptno) tmp
where DEPT.deptno = tmp.deptno;

5. 合并查询

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

5.1 UNION

union 用于取得两个结果集的并集,并自动去掉重复行。

案例:将工资大于 2500 或职位是 MANAGER 的人找出来。

sql 复制代码
select ename, sal, job from EMP where sal > 2500
union
select ename, sal, job from EMP where job = 'MANAGER';

5.2 UNION ALL

union all 同样用于合并结果集,但不会去重。

sql 复制代码
select ename, sal, job from EMP where sal > 2500
union all
select ename, sal, job from EMP where job = 'MANAGER';

使用 union 时需要注意:两个 select 的列数、列类型必须一致,否则会报错。


相关推荐
Rain的Java大神之路1 小时前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
荆棘鸟智能1 小时前
城市感知设备怎么统一接入?从多协议网关到设备模型的中间件架构设计
人工智能·算法·边缘计算
无敌贵点大王2 小时前
RTThread学习记录11——RT-Thread 设备模型吃透:UART/ADC/PWM/PIN 到底有什么区别?
c语言·stm32·学习·链表
Wang's Blog2 小时前
Java 接入Redis: 通用命令与键管理
java·服务器·redis
Wang's Blog2 小时前
Java 接入Redis: 列表集合与有序集合操作命令
java·服务器·redis
Logic1012 小时前
C语言/数据结构位运算题解:异或XOR找出时尚聚会中的“独特颜色“——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
Ivanqhz2 小时前
SVD++算法
java·服务器·网络·深度学习·神经网络
码事漫谈3 小时前
别再跟AI说“请”了,它根本不 care——但有个东西它超在意
后端
qq_2518364573 小时前
springboot vue3 开发实现 拼豆管理系统
java·开发语言·ai编程