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 | 否 | 高 | 允许重复 / 确定无重复 |
六、复合查询核心总结
- 多表查询一定要加连接条件,避免笛卡尔积
- 自连接 = 同表起别名,处理层级关系
- 子查询分:单行 / 多行 / 多列 / FROM 子查询
IN / ANY / ALL专门处理多行子查询UNION去重,UNION ALL性能更高- 分组后过滤用
HAVING,不是WHERE
七、学习建议
- 先把本文案例手敲一遍
- 用
EXPLAIN看执行计划,理解查询原理 - 多刷牛客 / LeetCode SQL 专题,强化手感
复合查询是 MySQL 最核心、面试最高频的知识点,吃透它,你的 SQL 水平会直接上一个台阶。
相关文章
Mysql中校对集utf8_unicode_ci与utf8_general_ci的区别说明
一直对utf8_unicode_ci与utf8_general_ci这2个校对集很迷惑,今天查了手册有了点眉目。不过对中文字符集来说采用utf8_unicode_ci与utf8_general_ci时有何区别还是不清楚2012-03-03
MYSQL出现" Client does not support authentication "的
MYSQL出现" Client does not support authentication "的解决方法...2007-06-06


最新评论