MySQL数据库中复合查询的操作详解

 更新时间:2026年05月12日 09:05:14   作者:重生之小比特  
在 MySQL 日常开发里,单表查询只能处理最简单的数据需求,真正的业务场景几乎都要用到复合查询,本文就来系统讲解一下MySQL复合查询的核心技术与实战应用吧

在 MySQL 日常开发里,单表查询只能处理最简单的数据需求,真正的业务场景几乎都要用到复合查询—— 也就是多表关联、嵌套查询、自连接、结果合并这类高级查询。

今天这篇文章,我就带着大家把复合查询从基础到实战彻底讲透,每一个知识点都配案例 + 解释,小白也能轻松学会。

一、先回顾:单表基础查询(温故知新)

复合查询是单表查询的进阶,我们先用几个经典案例快速过一遍重点语法。

1. 多条件筛选

查询工资高于 500 岗位是 MANAGER,且姓名以 J 开头的员工:

SELECT * FROM EMP 
WHERE (sal>500 OR job='MANAGER') 
AND ename LIKE 'J%';

2. 多字段排序

按部门号升序、同部门内工资降序:

SELECT * FROM EMP ORDER BY deptno asc, sal DESC;

3. 计算年薪并排序

奖金为空时用 IFNULL 转 0,避免计算错误:

SELECT ename, sal*12+IFNULL(comm,0) AS '年薪' 
FROM EMP 
ORDER BY 年薪 DESC;

4. 聚合函数搭配子查询

查工资最高的员工:

SELECT ename, job FROM EMP 
WHERE sal = (SELECT MAX(sal) FROM EMP);

查高于平均工资的员工:

SELECT ename, sal FROM EMP 
WHERE sal > (SELECT AVG(sal) FROM EMP);

5. 分组统计 + 分组后过滤

每个部门平均工资(保留两位小数)、最高工资:

SELECT deptno, FORMAT(AVG(sal), 2), MAX(sal) 
FROM EMP 
GROUP BY deptno;

这里的FORMAT 是 MySQL 里专门用来「格式化数字 / 日期」的函数,最常用作用是:把数字保留指定位小数、加千分位分隔符。标准格式:FORMAT(数字, 保留小数位数)。

平均工资低于 2000 的部门:

SELECT deptno, AVG(sal) AS avg_sal 
FROM EMP 
GROUP BY deptno 
HAVING avg_sal < 2000;

二、多表查询:跨表取数的核心

实际开发中,数据分散在多张表里,必须用多表连接才能拿到完整信息。

本文用经典 3 张表演示:

  • EMP:员工表(员工号、姓名、岗位、工资、部门号…)
  • DEPT:部门表(部门号、部门名、位置…)
  • SALGRADE:工资等级表(等级、最低工资、最高工资)

1. 什么是笛卡尔积

不加连接条件直接查多张表,会出现全组合,数据量爆炸,绝对不能用

-- 错误示例:产生笛卡尔积
SELECT * FROM EMP, DEPT;

2. 正确多表查询(内连接)

必须加上关联条件(通常是外键 = 主键)。

案例 1:员工名、工资、所在部门名

SELECT EMP.ename, EMP.sal, DEPT.dname 
FROM EMP, DEPT 
WHERE EMP.deptno = DEPT.deptno;

案例 2:只看 10 号部门的员工与部门名

SELECT ename, sal, dname 
FROM EMP, DEPT 
WHERE EMP.deptno = DEPT.deptno 
AND DEPT.deptno = 10;

案例 3:员工姓名、工资、工资等级

SELECT ename, sal, grade 
FROM EMP, SALGRADE 
WHERE EMP.sal BETWEEN losal AND hisal;

三、自连接:一张表自己连自己

自连接:同一张表起两个别名,当成两张表用。典型场景:员工与领导关系员工表的 mgr 指向领导的 empno)。

案例:查员工 FORD 的上级编号与姓名

方式 1:子查询

SELECT empno, ename 
FROM emp 
WHERE empno = (SELECT mgr FROM emp WHERE ename='FORD');

方式 2:自连接(更优雅)

SELECT leader.empno, leader.ename 
FROM emp leader, emp worker 
WHERE leader.empno = worker.mgr 
AND worker.ename='FORD';

要点:给表起别名,区分 “领导表”leader 和 “员工表”worker。

四、子查询(嵌套查询):复合查询灵魂

子查询把一个 SELECT 嵌套在另一个 SQL 里,先执行内层,再执行外层

1. 单行子查询(返回 1 行 1 列)

用于 = > < >= <= 这类单值比较。

案例:和 SMITH 同一部门的员工

SELECT * FROM EMP 
WHERE deptno = (SELECT deptno FROM EMP WHERE ename='SMITH');

2. 多行子查询(返回多行 1 列)

必须搭配 IN / ANY / ALL 使用。

① IN(在结果列表里)

查询和 10 部门岗位相同,但不属于 10 部门的员工:

SELECT ename, job, sal, deptno 
FROM emp 
WHERE job IN (SELECT DISTINCT job FROM emp WHERE deptno=10) 
AND deptno != 10;

② ALL(比所有都…)

工资比 30 部门所有人都高的员工:

SELECT ename, sal, deptno 
FROM EMP 
WHERE sal > ALL(SELECT sal FROM EMP WHERE deptno=30);

③ ANY(比任意一个…)

工资比 30 部门任意一人高即可:

SELECT ename, sal, deptno 
FROM EMP 
WHERE sal > ANY(SELECT sal FROM EMP WHERE deptno=30);

3. 多列子查询(返回多列)

同时匹配多个字段,用 (字段1, 字段2) = (子查询列1, 列2)

案例:和 SMITH 部门、岗位完全相同的人(排除 SMITH):

SELECT ename FROM EMP 
WHERE (deptno, job) = (SELECT deptno, job FROM EMP WHERE ename='SMITH') 
AND ename <> 'SMITH';

4. FROM 里的子查询(临时表 / 派生表)

把子查询结果当临时表使用,非常适合先分组统计、再关联查询

案例 1:高于本部门平均工资的员工

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:每个部门工资最高的人

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:部门信息 + 部门人数

SELECT DEPT.deptno, dname, mycnt, loc 
FROM DEPT, 
  (SELECT COUNT(*) mycnt, deptno FROM EMP GROUP BY deptno) tmp 
WHERE DEPT.deptno = tmp.deptno;

五、合并查询:UNION 与 UNION ALL

多个 SELECT 结果纵向拼接,要求:

  • 列数相同
  • 对应列类型兼容
  • 列名以第一个 SELECT 为准

1. UNION:合并并自动去重

SELECT ename, sal, job FROM EMP WHERE sal>2500
UNION
SELECT ename, sal, job FROM EMP WHERE job='MANAGER';

2. UNION ALL:直接合并,不去重

性能比 UNION 高很多,确定无重复时优先用它。

SELECT ename, sal, job FROM EMP WHERE sal>2500
UNION ALL
SELECT ename, sal, job FROM EMP WHERE job='MANAGER';

对比速记

关键字是否去重性能适用场景
UNION较低需去重
UNION ALL允许重复 / 确定无重复

六、复合查询核心总结

  1. 多表查询一定要加连接条件,避免笛卡尔积
  2. 自连接 = 同表起别名,处理层级关系
  3. 子查询分:单行 / 多行 / 多列 / FROM 子查询
  4. IN / ANY / ALL 专门处理多行子查询
  5. UNION 去重,UNION ALL 性能更高
  6. 分组后过滤用 HAVING,不是 WHERE

七、学习建议

  • 先把本文案例手敲一遍
  • EXPLAIN 看执行计划,理解查询原理
  • 多刷牛客 / LeetCode SQL 专题,强化手感

复合查询是 MySQL 最核心、面试最高频的知识点,吃透它,你的 SQL 水平会直接上一个台阶。

相关文章

  • 解决MySQL存储时间出现不一致的问题

    解决MySQL存储时间出现不一致的问题

    这篇文章主要介绍了解决MySQL存储时间出现不一致的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • 解决Navicat导入数据库数据结构sql报错datetime(0)的问题

    解决Navicat导入数据库数据结构sql报错datetime(0)的问题

    这篇文章主要介绍了解决Navicat导入数据库数据结构sql报错datetime(0)的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07
  • QT连接Mysql数据库的详细教程(亲测成功版)

    QT连接Mysql数据库的详细教程(亲测成功版)

    被Qt连接数据库折磨了三天之后终于连接成功了,记录一下希望对看到的人有所帮助,下面这篇文章主要给大家介绍了关于QT连接Mysql数据库的详细教程,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • 如何用word vb宏来生成sql

    如何用word vb宏来生成sql

    本篇文章是对利用word vb宏来生成sql的方法进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • mysql清除log-bin日志的方法

    mysql清除log-bin日志的方法

    这篇文章主要介绍了mysql清除log-bin日志的方法,同时介绍了log-bin日志的作用,需要的朋友可以参考下
    2014-06-06
  • MySql连接数据库常用参数及代码解读

    MySql连接数据库常用参数及代码解读

    这篇文章主要介绍了MySql连接数据库常用参数及代码解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • MYSQL主库切换binlog模式后主从同步错误的解决方案

    MYSQL主库切换binlog模式后主从同步错误的解决方案

    在使用FlinkSQL的mysql-cdc连接器来监听MySQL数据库时,通常需要将MySQL的binlog模式设置为ROW模式,当我们将MySQL主库的binlog模式从STATEMENT切换为ROW并重启MySQL服务后,MySQL从库在同步时可能会报错,所以本文介绍了MYSQL主库切换binlog模式后主从同步错误的解决方案
    2024-08-08
  • 详细聊聊MySQL中auto_increment有什么作用

    详细聊聊MySQL中auto_increment有什么作用

    auto_increment是用于主键自动增长的,从1开始增长,下面这篇文章主要给大家介绍了关于MySQL中auto_increment有什么作用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • Mysql中LEFT JOIN和JOIN查询区别及原理详解

    Mysql中LEFT JOIN和JOIN查询区别及原理详解

    这篇文章主要介绍了Mysql中LEFT JOIN和JOIN查询区别及原理详解,Nested Loop Join 实际上就是通过驱动表的结果集作为循环基础数据,然后一条一条的通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果,需要的朋友可以参考下
    2023-08-08
  • Mysql误删数据解决方案及kill语句原理

    Mysql误删数据解决方案及kill语句原理

    这篇文章主要介绍了Mysql误删数据解决方案及kill语句原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09

最新评论