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子句

四、总结

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

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

相关推荐
Zzzzmo_3 小时前
【MySQL】JDBC(含settings.xml文件配置/配置国内镜像以及pom.xml文件修改)
数据库·mysql
FirstFrost --sy4 小时前
MySQL内置函数
数据库·mysql
Simon_lca5 小时前
突破合规瓶颈:ZDHC Supplier to Zero(工厂零排放 - 进阶型)体系全攻略
大数据·网络·人工智能·分类·数据挖掘·数据分析·零售
eggwyw5 小时前
MySQL-练习-数据汇总-CASE WHEN
数据库·mysql
mygljx7 小时前
MySQL 数据库连接池爆满问题排查与解决
android·数据库·mysql
Bdygsl8 小时前
MySQL(1)—— 基本概念和操作
数据库·mysql
身如柳絮随风扬8 小时前
什么是左匹配规则?
数据库·sql·mysql
jiankeljx9 小时前
mysql之如何获知版本
数据库·mysql
小李来了!9 小时前
数据库DDL、DML、DQL、DCL详解
数据库·mysql
我科绝伦(Huanhuan Zhou)10 小时前
【生产案例】MySQL InnoDB 数据损坏崩溃修复
数据库·mysql·adb