MySQL 多表查询中,联合查询(UNION) 和子查询

一、联合查询(UNION):合并多个查询结果

联合查询用于将多个独立查询的结果集合并为一个结果集,适用于 "需要从不同表或同一表的不同条件下获取结构一致的数据" 的场景(如合并 "研发部员工" 和 "薪资 > 10000 的员工")。

1. 核心语法与规则

sql

sql 复制代码
-- 语法
查询语句1
UNION [ALL]
查询语句2
[UNION [ALL] 查询语句3 ...]
  • 关键规则 :① 多个查询的列数必须一致 (否则报错);② 列的数据类型需兼容 (如intdecimal可兼容,intvarchar通常不兼容);③ UNION会自动去重 (删除完全相同的记录);UNION ALL不进行去重,直接合并(性能更好,推荐在确认无重复时使用)。
2. 代码示例

基于emp表数据(部分员工信息如下):

id name age dept_id salary
1 金庸 66 5 20000
2 张无忌 20 1 12500
3 杨晓 33 1 8400
4 韦一笑 66 1 11000
5 常遇春 43 1 10500
6 小昭 19 1 6000

需求 1:查询 "研发部(dept_id=1)的员工" 和 "薪资 > 10000 的员工",合并结果(去重)。

sql

sql 复制代码
-- 子查询1:研发部员工(dept_id=1)
select name, age, dept_id, salary from emp where dept_id = 1
union  -- 去重合并
-- 子查询2:薪资>10000的员工
select name, age, dept_id, salary from emp where salary > 10000;
  • 结果分析:研发部员工包括张无忌、杨晓、韦一笑、常遇春、小昭;薪资 > 10000 的员工包括金庸(20000)、张无忌(12500)、韦一笑(11000)、常遇春(10500)。合并后去重,最终结果包含:张无忌、杨晓、韦一笑、常遇春、小昭、金庸(共 6 条,无重复)。

需求 2 :同上,但保留重复记录(使用UNION ALL)。

sql

sql 复制代码
select name, age, dept_id, salary from emp where dept_id = 1
union all  -- 不查重,直接合并
select name, age, dept_id, salary from emp where salary > 10000;
  • 结果分析:张无忌、韦一笑、常遇春同时满足两个条件,会被重复显示,最终结果共5(研发部)+4(高薪)=9条。

二、子查询:嵌套在其他查询中的查询

子查询(Subquery)是嵌套在SELECTINSERTUPDATEDELETE等语句中的查询。其中,外层的查询称为 "主查询",内层的查询称为 "子查询"。

子查询的作用是:先通过子查询获取一个中间结果,再用主查询基于这个结果做进一步处理。根据子查询返回结果的形状,可分为以下 4 类:

1. 标量子查询:返回 "一行一列"(单一值)

标量子查询是最常用的子查询类型,返回结果为单个值 (如一个数字、一个字符串),通常用于WHERE子句中,作为条件的一部分(配合=><等比较运算符)。

特点

  • 结果必须是 "一行一列"(若返回多行 / 多列会报错);
  • 可理解为 "用子查询的结果作为一个变量" 参与主查询的条件判断。

代码示例 需求 :查询与 "张无忌" 同部门的员工(张无忌的dept_id=1)。

步骤:① 先通过子查询获取张无忌的部门 id(dept_id=1);② 主查询基于该部门 id,查询所有同部门员工。

sql

sql 复制代码
-- 标量子查询:子查询返回张无忌的dept_id(单一值1)
select * from emp 
where dept_id = (select dept_id from emp where name = '张无忌');
  • 结果:返回dept_id=1的所有员工(张无忌、杨晓、韦一笑、常遇春、小昭)。

注意 :若子查询可能返回NULL,需避免直接用=(可能无结果),可改用IS NULL;若子查询可能返回多行,需用IN(而非=,否则报错)。

2. 列子查询:返回 "一列多行"

列子查询返回结果为一列数据(多行) ,通常用于WHERE子句中,配合INNOT INANYALL等关键字使用。

常用关键字

  • IN:主查询结果在子查询返回的列中(如x IN (1,2,3));
  • NOT IN:主查询结果不在子查询返回的列中;
  • ANY:主查询结果满足子查询返回列中的任意一个(如x > ANY(1,3,5)x>1即可);
  • ALL:主查询结果满足子查询返回列中的所有值(如x > ALL(1,3,5)x>5才满足)。

代码示例 需求 1 :查询 "研发部(dept_id=1)" 或 "市场部(dept_id=2)" 的员工(用IN)。

sql

sql 复制代码
-- 列子查询:返回部门id为1和2(一列两行)
select * from emp 
where dept_id in (select id from dept where name in ('研发部', '市场部'));
  • 子查询解析:select id from dept where name in ('研发部', '市场部')返回12(一列两行);
  • 主查询:筛选dept_id为 1 或 2 的员工(即研发部所有员工,市场部若有员工也会被包含)。

需求 2 :查询年龄比研发部(dept_id=1)所有员工都大的员工(用ALL)。

sql

sql 复制代码
-- 列子查询:返回研发部所有员工的年龄(一列多行:20,33,66,43,19)
select * from emp 
where age > all (select age from emp where dept_id = 1);
  • 子查询结果:研发部员工年龄为20,33,66,43,19
  • 主查询条件age > all(...) → 年龄需大于最大的 66 → 最终只有金庸(66?不,66 不大于 66,所以无结果,可调整为age >= all(...)返回金庸)。
3. 行子查询:返回 "一行多列"

行子查询返回结果为一行数据(多列) ,通常用于WHERE子句中,配合=IN等运算符,匹配主查询中 "多列组合" 的条件。

特点

  • 结果是 "一行多列"(如(dept_id, managerid) = (1, 2));
  • 适用于 "需要同时匹配多个字段" 的场景(如 "同部门且同领导")。

代码示例 需求 :查询与 "杨晓" 同部门且同领导的员工(杨晓的dept_id=1managerid=2)。

sql

sql 复制代码
-- 行子查询:返回杨晓的dept_id和managerid(一行两列:(1,2))
select * from emp 
where (dept_id, managerid) = (select dept_id, managerid from emp where name = '杨晓');
  • 子查询结果:(1,2)(杨晓的部门 id=1,领导 id=2);
  • 主查询条件:(dept_id, managerid) = (1,2) → 匹配的员工包括杨晓、韦一笑(id=4,dept_id=1managerid=2)、小昭(id=6,dept_id=1managerid=2)。
4. 表子查询:返回 "多行多列"

表子查询返回结果为多行多列 (相当于一个临时表),通常用于FROM子句中,作为主查询的 "数据源"(需给子查询起别名)。

特点

  • 结果是一个 "临时表"(多行多列),可像普通表一样参与连接、筛选等操作;
  • 适用于 "需要先对数据做聚合 / 过滤,再与其他表关联" 的场景。

代码示例 需求 :查询每个部门的平均薪资,以及部门名称(需关联dept表)。

步骤:① 先通过表子查询计算每个部门的平均薪资(dept_idavg_salary,多行多列);② 主查询将子查询结果与dept表连接,获取部门名称。

sql

sql 复制代码
-- 表子查询:计算每个部门的平均薪资(临时表t)
select d.name as 部门名称, t.avg_salary as 平均薪资 
from (select dept_id, avg(salary) as avg_salary from emp group by dept_id) as t  -- 表子查询必须起别名
join dept d on t.dept_id = d.id;  -- 与部门表连接
  • 子查询解析:select dept_id, avg(salary) ... group by dept_id返回每个部门的平均薪资(如dept_id=1的平均薪资为(12500+8400+11000+10500+6000)/5=9680);
  • 主查询:将临时表tdept表连接,通过dept_id关联,最终显示 "部门名称 + 平均薪资"。

三、子查询与联合查询的对比

类型 核心作用 适用场景 关键特点
联合查询 合并多个查询的结果集 结果结构一致的多条件数据合并 列数需一致,UNION去重,UNION ALL高效
标量子查询 返回单一值,作为条件变量 基于单个值的筛选(如 "同部门") 一行一列,配合=>
列子查询 返回一列多行,用于集合匹配 基于多个值的筛选(如 "在某些部门中") 一列多行,配合INANYALL
行子查询 返回一行多列,用于多字段匹配 同时匹配多个字段(如 "同部门且同领导") 一行多列,配合(a,b) = (子查询)
表子查询 返回多行多列,作为临时数据源 先聚合 / 过滤,再关联其他表 多行多列,需起别名,用于FROM子句

四、总结

  • 联合查询是 "横向合并" 结果集,核心是保证多查询的列结构一致;
  • 子查询是 "嵌套查询",通过内层查询的结果辅助外层查询,根据返回结果的形状分为标量、列、行、表四种,分别对应不同的使用场景和运算符。

掌握这些技术后,可灵活处理复杂的多表查询需求(如 "查询每个部门薪资最高的员工,且该员工年龄大于部门平均年龄" 等)。

相关推荐
B站_计算机毕业设计之家4 小时前
机器学习实战项目:Python+Flask 汽车销量分析可视化系统(requests爬车主之家+可视化 源码+文档)✅
人工智能·python·机器学习·数据分析·flask·汽车·可视化
先鱼鲨生4 小时前
【MySQL】认识数据库以及MySQL安装
数据库·mysql
周杰伦_Jay4 小时前
【终端使用MySQL】MySQL 数据库核心操作全解析:从入门到性能优化
数据库·mysql·性能优化
SelectDB5 小时前
Apache Doris 内部数据裁剪与过滤机制的实现原理
数据库·数据分析·github
SelectDB5 小时前
岚图汽车 x Apache Doris : 海量车联网数据实时分析实践
数据分析·github·数据可视化
-雷阵雨-6 小时前
MySQL——数据库入门指南
数据库·mysql
就叫飞六吧7 小时前
DataX适合全量同步和简单的增量场景
mysql
xhbh6668 小时前
【实战避坑】MySQL自增主键(AUTO_INCREMENT)全解:从锁机制、间隙问题到分库分表替代方案
android·数据库·mysql·mysql自增主键
葡萄城技术团队10 小时前
零基础上手数据分析工具:从入门到实操全指南
数据分析