MySQL 复合查询核心指南:多表、子查询与实战技巧


🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!


🎬 博主简介:


文章目录

  • 前言:
  • [一. 基础回顾:复合查询的前置知识](#一. 基础回顾:复合查询的前置知识)
  • [二. 多表查询:跨表关联核心玩法](#二. 多表查询:跨表关联核心玩法)
    • [2.1 测试表结构](#2.1 测试表结构)
    • [2.2 多表查询核心语法](#2.2 多表查询核心语法)
    • [2.3 实战案例](#2.3 实战案例)
      • [2.3.1 关联两张表:员工 + 部门信息](#2.3.1 关联两张表:员工 + 部门信息)
      • [2.3.2 多表 + 条件筛选:指定部门员工信息](#2.3.2 多表 + 条件筛选:指定部门员工信息)
      • [2.3.3 关联多张表:员工 + 薪资等级](#2.3.3 关联多张表:员工 + 薪资等级)
    • [2.4 多表查询避坑点](#2.4 多表查询避坑点)
  • [三. 自连接:同表关联查询](#三. 自连接:同表关联查询)
    • [3.1 核心语法](#3.1 核心语法)
    • [3.2 实战案例:查询员工的上级领导](#3.2 实战案例:查询员工的上级领导)
    • [3.3 自连接关键技巧](#3.3 自连接关键技巧)
  • [四. 子查询:嵌套查询的灵活用法](#四. 子查询:嵌套查询的灵活用法)
    • [4.1 单行子查询:返回 1 行 1 列结果](#4.1 单行子查询:返回 1 行 1 列结果)
    • [4.2 多行子查询:返回多行 1 列结果](#4.2 多行子查询:返回多行 1 列结果)
      • [4.2.1 in 关键字:匹配多个值中的任意一个](#4.2.1 in 关键字:匹配多个值中的任意一个)
      • [4.2.2 all 关键字:大于 / 小于所有值](#4.2.2 all 关键字:大于 / 小于所有值)
      • [4.2.3 any 关键字:大于 / 小于任意一个值](#4.2.3 any 关键字:大于 / 小于任意一个值)
    • [4.3 多列子查询:返回多行多列结果](#4.3 多列子查询:返回多行多列结果)
    • [4.4 from 子句中的子查询:临时表用法](#4.4 from 子句中的子查询:临时表用法)
    • [4.5 子查询避坑指南](#4.5 子查询避坑指南)
  • [五. 合并查询:union 与 union all](#五. 合并查询:union 与 union all)
    • [5.1 核心语法](#5.1 核心语法)
    • [5.2 实战案例](#5.2 实战案例)
  • [六. 实战 OJ 真题:复合查询落地应用](#六. 实战 OJ 真题:复合查询落地应用)
  • [七. 总结](#七. 总结)
  • 结尾:

前言:

在实际开发中,单表查询远不能满足复杂业务需求 ------ 员工信息散落在员工表、部门表、薪资等级表中,需要跨表关联才能获取完整数据;统计分析时需嵌套查询筛选条件;多结果集合并需用到集合操作符。本文全面拆解 MySQL 复合查询的核心玩法,包括多表查询、自连接、子查询、合并查询,所有 SQL 均采用小写形式,贴合开发规范,附带实战案例和避坑要点


一. 基础回顾:复合查询的前置知识

在学习复杂复合查询前,先回顾基础查询的核心语法,为后续进阶打基础:

sql 复制代码
-- 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. 计算字段+排序:年薪(sal*12+补贴)降序
select ename, sal*12+ifnull(comm,0) as 年薪 from emp order by 年薪 desc;

-- 4. 聚合查询+筛选:各部门平均工资(保留2位小数)和最高工资
select deptno, format(avg(sal),2) as 平均工资, max(sal) as 最高工资 from emp group by deptno;

-- 5. having筛选聚合结果:平均工资低于2000的部门
select deptno, avg(sal) as avg_sal from emp group by deptno having avg_sal < 2000;

二. 多表查询:跨表关联核心玩法

多表查询是复合查询的基础,用于从多个关联表中提取数据,核心是通过 "关联字段" 消除笛卡尔积(无关联条件时,表 1 所有行与表 2 所有行组合,数据量爆炸)。

2.1 测试表结构

本次实战基于 3 张经典表,先明确表结构和关联关系:

  • emp(员工表) :存储员工基本信息,关联字段deptno(关联部门表)、sal(关联薪资等级表);
  • dept(部门表) :存储部门信息,关联字段deptno
  • salgrade(薪资等级表) :存储薪资等级规则,关联字段losal(最低工资)、hisal(最高工资)。

2.2 多表查询核心语法

sql 复制代码
select 表1.字段, 表2.字段 
from 表1, 表2 
where 表1.关联字段 = 表2.关联字段 [and 其他筛选条件];

2.3 实战案例

2.3.1 关联两张表:员工 + 部门信息

需求:查询员工姓名、工资及所在部门名称

sql 复制代码
-- 核心:通过deptno关联emp和dept表,消除笛卡尔积
select emp.ename, emp.sal, dept.dname 
from emp, dept 
where emp.deptno = dept.deptno;

2.3.2 多表 + 条件筛选:指定部门员工信息

需求:查询 10 号部门的员工姓名、工资及部门名称

sql 复制代码
select emp.ename, emp.sal, dept.dname 
from emp, dept 
where emp.deptno = dept.deptno 
  and dept.deptno = 10; -- 筛选10号部门

2.3.3 关联多张表:员工 + 薪资等级

需求:查询员工姓名、工资及对应的薪资等级

sql 复制代码
select emp.ename, emp.sal, salgrade.grade 
from emp, salgrade 
where emp.sal between salgrade.losal and salgrade.hisal; -- 工资在薪资等级区间内

2.4 多表查询避坑点

  • 必须加关联条件:无关联条件会产生笛卡尔积(如 emp 有 14 行、dept 有 4 行,会产生 14×4=56 行无效数据);
  • 字段歧义需加表名前缀 :若多个表有同名字段(如deptno),需用表名.字段区分;
  • 关联字段类型必须一致:emp.deptno 和 dept.deptno 需同为 int 类型,否则关联失效。

三. 自连接:同表关联查询

自连接是多表查询的特殊形式 ------ 将同一张表当作两张表使用,通过别名区分,适用于查询表内关联数据(如员工与上级领导的关系)。

3.1 核心语法

sql 复制代码
select 表别名1.字段, 表别名2.字段 
from 表 表别名1, 表 表别名2 
where 表别名1.关联字段 = 表别名2.关联字段 [and 筛选条件];

3.2 实战案例:查询员工的上级领导

需求:查询员工 ford 的上级领导编号和姓名(emp 表中mgr字段是领导的empno

sql 复制代码
-- 方法1:子查询(简单场景)
select empno, ename from emp 
where empno = (select mgr from emp where ename='ford');

-- 方法2:自连接(复杂场景更灵活)
select leader.empno as 领导编号, leader.ename as 领导姓名 
from emp leader, emp worker -- leader=领导表,worker=员工表
where leader.empno = worker.mgr -- 领导编号=员工的上级编号
  and worker.ename = 'ford'; -- 筛选员工为ford

3.3 自连接关键技巧

  • 必须给表起不同别名(如leaderworker),否则 MySQL 无法区分两张 "虚拟表";
  • 关联字段需是表内的关联关系(如员工表的mgr与自身的empno)。

四. 子查询:嵌套查询的灵活用法

子查询(嵌套查询)是指嵌入在其他 SQL 语句中的 select 语句,按返回结果可分为单行、多行、多列子查询,按位置可分为 where 子句、from 子句中的子查询。

4.1 单行子查询:返回 1 行 1 列结果

适用于筛选条件为 "等于、大于、小于" 单个值的场景,常用比较运算符(=、>、<、>=、<=)。

实战案例:
需求:查询与 smith 同部门的所有员工(不含 smith)

sql 复制代码
select * from emp 
where deptno = (select deptno from emp where ename='smith') -- 子查询返回smith的部门号
  and ename != 'smith'; -- 排除smith本人

4.2 多行子查询:返回多行 1 列结果

适用于筛选条件为 "在多个值中""大于所有值""大于任意值" 的场景,常用关键字in、all、any

4.2.1 in 关键字:匹配多个值中的任意一个

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

sql 复制代码
select ename, job, sal, deptno from emp 
where job in (select distinct job from emp where deptno=10) -- 子查询返回10号部门的所有岗位
  and deptno != 10; -- 排除10号部门

4.2.2 all 关键字:大于 / 小于所有值

需求:查询工资比 30 号部门所有员工工资都高的员工

sql 复制代码
select ename, sal, deptno from emp 
where sal > all(select sal from emp where deptno=30); -- 工资>30号部门所有员工工资

4.2.3 any 关键字:大于 / 小于任意一个值

需求:查询工资比 30 号部门任意员工工资高的员工(含自身部门)

sql 复制代码
select ename, sal, deptno from emp 
where sal > any(select sal from emp where deptno=30); -- 工资>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 子句中的子查询:临时表用法

将子查询结果当作 "临时表",用于复杂统计分析(如先聚合再关联),核心是给临时表起别名。

实战案例 1:查询高于本部门平均工资的员工

sql 复制代码
select emp.ename, emp.deptno, emp.sal, format(tmp.asal,2) as 部门平均工资 
from emp, 
     (select avg(sal) as asal, deptno as dt from emp group by deptno) tmp -- 临时表:各部门平均工资
where emp.deptno = tmp.dt -- 员工部门=临时表部门
  and emp.sal > tmp.asal; -- 员工工资>部门平均工资

实战案例 2:查询各部门工资最高的员工

sql 复制代码
select emp.ename, emp.sal, emp.deptno, tmp.ms as 部门最高工资 
from emp, 
     (select max(sal) as ms, deptno from emp group by deptno) tmp -- 临时表:各部门最高工资
where emp.deptno = tmp.deptno 
  and emp.sal = tmp.ms;

4.5 子查询避坑指南

  • 单行子查询只能用单行运算符 :若子查询返回多行,不能用=,需用in
  • from 子句的子查询必须起别名:MySQL 要求临时表必须有别名,否则报错;
  • 子查询尽量简化:复杂子查询可拆分为临时表或多步查询,提升可读性和性能。

五. 合并查询:union 与 union all

合并查询用于将多个 select 语句的结果集合并为一个,适用于多条件独立查询后合并结果的场景,核心是union(去重)和union all(不去重)。

5.1 核心语法

sql 复制代码
-- 去重合并(自动删除重复行)
select 字段 from 表1 where 条件
union
select 字段 from 表2 where 条件;

-- 不去重合并(保留重复行,性能更优)
select 字段 from 表1 where 条件
union all
select 字段 from 表2 where 条件;

5.2 实战案例

案例 1:union 去重合并
需求:查询工资 > 2500 或岗位为 manager 的员工(去重)

sql 复制代码
select ename, sal, job from emp where sal>2500
union -- 自动去重(manager中工资>2500的员工只显示一次)
select ename, sal, job from emp where job='manager';

案例 2:union all 不去重合并
需求:查询工资 > 2500 或岗位为 manager 的员工(保留重复)

sql 复制代码
select ename, sal, job from emp where sal>2500
union all -- 保留重复行(manager中工资>2500的员可能会显示多次)
select ename, sal, job from emp where job='manager';

六. 实战 OJ 真题:复合查询落地应用

结合牛客网经典 OJ 题,练习复合查询的实际应用:
真题 1:查找所有员工入职时的薪水情况(emp_no+salary,逆序)

sql 复制代码
select e.emp_no, s.salary 
from employees e, salaries s 
where e.emp_no = s.emp_no 
  and s.from_date = e.hire_date 
order by e.emp_no desc;

真题 2:生成所有表的 count 查询语句

sql 复制代码
select concat('select count(*) from ', table_name, ';') as count_sql 
from information_schema.tables 
where table_schema = 'your_database_name'; -- 替换为你的数据库名

真题 3:获取所有非 manager 的员工 emp_no

sql 复制代码
select emp_no from employees
where emp_no not in(select emp_no from dept_manager);

真题 4:获取所有员工当前的 manager(排除 manager 是自己的情况)

sql 复制代码
select e.emp_no, d.emp_no as manager 
from dept_emp as e, dept_manager as d 
where e.dept_no = d.dept_no 
  and e.emp_no != d.emp_no;

七. 总结

MySQL 复合查询是解决复杂业务需求的核心,核心要点总结:

  • 多表查询:通过关联字段消除笛卡尔积,适用于跨表提取数据;
  • 自连接:同表当作两张表,适用于表内关联(如员工与领导);
  • 子查询:嵌套在 where/from 子句中,灵活筛选和统计,需注意单行 / 多行匹配规则;
  • 合并查询:union(去重)和 union all(不去重),适用于多结果集合并;
  • 避坑关键:关联字段一致、临时表起别名、优先选择高效语法(如 union all 替代 union)。

结尾:

html 复制代码
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!

结语:掌握这些复合查询技巧,能应对 90% 以上的业务场景。建议多结合实际需求练习,逐步提升 SQL 的灵活性和性能优化能力。创作不易,觉得有帮助的话,欢迎点赞、收藏、关注三连~ 后续会持续更新 MySQL 索引优化、事务、存储过程等进阶内容,带你从入门到精通数据库开发。

✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど

相关推荐
johnsapzp2 小时前
Redis--模糊查询--方法实例
数据库·redis·缓存
kaoa0002 小时前
Linux入门攻坚——71、puppet-1
linux·运维·puppet
2501_908329852 小时前
使用Python分析你的Spotify听歌数据
jvm·数据库·python
小杨啥都学2 小时前
通过ipsec服务端给客户端分配ip
服务器·网络·tcp/ip·ipsec
GAOJ_K2 小时前
旋转花键在高端制造中的差异化应用
运维·人工智能·科技·机器人·自动化·制造
3DVisionary2 小时前
复杂工况下的力学透视:三维全场动态变形视觉检测系统解析金属3D打印
人工智能·3d·视觉检测·增材制造·轻量化结构·全场动态变形监测·复杂工况测试
炸炸鱼.2 小时前
MySQL 索引与事务核心知识点
数据库·mysql
Xiaoweidumpb2 小时前
win10 Windows服务器开放端口防火墙规则 远程控制桌面
运维·服务器·windows
you-_ling2 小时前
网络:4.TCP并发服务器
服务器·网络·tcp/ip