一、联合查询(UNION):合并多个查询结果
联合查询用于将多个独立查询的结果集合并为一个结果集,适用于 "需要从不同表或同一表的不同条件下获取结构一致的数据" 的场景(如合并 "研发部员工" 和 "薪资 > 10000 的员工")。
1. 核心语法与规则
sql
sql
-- 语法
查询语句1
UNION [ALL]
查询语句2
[UNION [ALL] 查询语句3 ...]
- 关键规则 :① 多个查询的列数必须一致 (否则报错);② 列的数据类型需兼容 (如
int
与decimal
可兼容,int
与varchar
通常不兼容);③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)是嵌套在SELECT
、INSERT
、UPDATE
、DELETE
等语句中的查询。其中,外层的查询称为 "主查询",内层的查询称为 "子查询"。
子查询的作用是:先通过子查询获取一个中间结果,再用主查询基于这个结果做进一步处理。根据子查询返回结果的形状,可分为以下 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
子句中,配合IN
、NOT IN
、ANY
、ALL
等关键字使用。
常用关键字:
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 ('研发部', '市场部')
返回1
和2
(一列两行); - 主查询:筛选
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=1
,managerid=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=1
,managerid=2
)、小昭(id=6,dept_id=1
,managerid=2
)。
4. 表子查询:返回 "多行多列"
表子查询返回结果为多行多列 (相当于一个临时表),通常用于FROM
子句中,作为主查询的 "数据源"(需给子查询起别名)。
特点:
- 结果是一个 "临时表"(多行多列),可像普通表一样参与连接、筛选等操作;
- 适用于 "需要先对数据做聚合 / 过滤,再与其他表关联" 的场景。
代码示例 需求 :查询每个部门的平均薪资,以及部门名称(需关联dept
表)。
步骤:① 先通过表子查询计算每个部门的平均薪资(dept_id
和avg_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
); - 主查询:将临时表
t
与dept
表连接,通过dept_id
关联,最终显示 "部门名称 + 平均薪资"。
三、子查询与联合查询的对比
类型 | 核心作用 | 适用场景 | 关键特点 |
---|---|---|---|
联合查询 | 合并多个查询的结果集 | 结果结构一致的多条件数据合并 | 列数需一致,UNION 去重,UNION ALL 高效 |
标量子查询 | 返回单一值,作为条件变量 | 基于单个值的筛选(如 "同部门") | 一行一列,配合= 、> 等 |
列子查询 | 返回一列多行,用于集合匹配 | 基于多个值的筛选(如 "在某些部门中") | 一列多行,配合IN 、ANY 、ALL |
行子查询 | 返回一行多列,用于多字段匹配 | 同时匹配多个字段(如 "同部门且同领导") | 一行多列,配合(a,b) = (子查询) |
表子查询 | 返回多行多列,作为临时数据源 | 先聚合 / 过滤,再关联其他表 | 多行多列,需起别名,用于FROM 子句 |
四、总结
- 联合查询是 "横向合并" 结果集,核心是保证多查询的列结构一致;
- 子查询是 "嵌套查询",通过内层查询的结果辅助外层查询,根据返回结果的形状分为标量、列、行、表四种,分别对应不同的使用场景和运算符。
掌握这些技术后,可灵活处理复杂的多表查询需求(如 "查询每个部门薪资最高的员工,且该员工年龄大于部门平均年龄" 等)。