【MySQL 数据库】复合查询

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

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


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

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

1. 多条件筛选

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

sql

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

2. 多字段排序

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

sql

复制代码
SELECT * FROM EMP ORDER BY deptno asc, sal DESC;

3. 计算年薪并排序

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

sql

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

4. 聚合函数搭配子查询

  • 查工资最高的员工:

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);

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

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

sql

复制代码
SELECT deptno, FORMAT(AVG(sal), 2), MAX(sal) 
FROM EMP 
GROUP BY deptno;
  • 这里的FORMAT 是 MySQL 里专门用来「格式化数字 / 日期」的函数,最常用作用是:把数字保留指定位小数、加千分位分隔符。标准格式:FORMAT(数字, 保留小数位数)。

  • 平均工资低于 2000 的部门:

sql

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

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

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

本文用经典 3 张表演示:

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

1. 什么是笛卡尔积

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

sql

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

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

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

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

sql

复制代码
SELECT EMP.ename, EMP.sal, DEPT.dname 
FROM EMP, DEPT 
WHERE EMP.deptno = DEPT.deptno;
案例 2:只看 10 号部门的员工与部门名

sql

复制代码
SELECT ename, sal, dname 
FROM EMP, DEPT 
WHERE EMP.deptno = DEPT.deptno 
AND DEPT.deptno = 10;
案例 3:员工姓名、工资、工资等级

sql

复制代码
SELECT ename, sal, grade 
FROM EMP, SALGRADE 
WHERE EMP.sal BETWEEN losal AND hisal;

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

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

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

  • 方式 1:子查询

sql

复制代码
SELECT empno, ename 
FROM emp 
WHERE empno = (SELECT mgr FROM emp WHERE ename='FORD');
  • 方式 2:自连接(更优雅)

sql

复制代码
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 同一部门的员工

sql

复制代码
SELECT * FROM EMP 
WHERE deptno = (SELECT deptno FROM EMP WHERE ename='SMITH');

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

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

① 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);

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

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

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

sql

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

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

sql

复制代码
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:合并并自动去重

sql

复制代码
SELECT ename, sal, job FROM EMP WHERE sal>2500
UNION
SELECT ename, sal, job FROM EMP WHERE job='MANAGER';

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

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

sql

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

对比速记

表格

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

六、实战 OJ 常考题型(练手必备)

  1. 查找所有员工入职时候的薪水情况_牛客题霸_牛客网
  2. 获取所有非manager的员工emp_no_牛客题霸_牛客网
  3. 获取所有员工当前的manager_牛客题霸_牛客网
  4. 【MySQL牛客】11.获取所有员工当前的manager-CSDN博客

这些题在牛客网非常高频,练完基本面试稳了。


七、复合查询核心总结

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

八、学习建议

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

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

相关推荐
今天AI了吗15 小时前
Python 基础语法(一):常量、变量、输入输出与运算符
开发语言·数据库·人工智能·python·sql·深度学习·机器学习
夜雪一千16 小时前
MySQL 全局锁是什么?原理、风险、备份踩坑完整实战
android·mysql·adb
lilian23316 小时前
Harmony os 技术实战|拼豆制图27:用单字符编码承载 50 张 70×70 图纸
前端·数据库·华为·harmonyos
皮卡丘不断更16 小时前
手机优先的 Personal Ledger:把账目、学习和复盘放到同一个入口
数据库·sqlite·fastapi·开源项目·个人效率
保卫大狮兄17 小时前
设备管理从台账到报废,完整生命周期一次讲清
数据库·设备管理·设备
数据安全技术观察17 小时前
数据动态脱敏:让脱敏策略跟着数据目录走
大数据·网络·数据库
小席是个热心肠18 小时前
Redis的自我学习
数据库·redis·学习
2601_9657984718 小时前
Salient Theme Setup Guide for Fast Creative WordPress Sites
数据库·web3·php·wordpress
@Mike@19 小时前
09-数据库学习笔记(数据库索引与过滤器)
数据库·笔记
程序员夏洛20 小时前
MySQL 中 count(*)、count(1) 和 count(字段名) 有什么区别?
数据库·mysql