MySQL无原生集合类型,集合查询指用UNION/INTERSECT/EXCEPT对多个SELECT结果集进行合并、交集或差集运算;UNION去重并默认排序,UNION ALL性能更高;INTERSECT和EXCEPT仅8.0.31+支持;IN、GROUP BY等非集合操作;ORDER BY须置于整个集合操作末尾。

MySQL 没有传统意义上的“集合数据类型”(比如 PostgreSQL 的 ARRAY 或 JSON 数组作为一等集合),所谓“MySQL 集合查询”,指的其实是用集合操作符把多个 SELECT 结果按数学集合逻辑合并、交集或差集处理——本质是结果集之间的横向运算,不是存储结构。
UNION / UNION ALL:最常用、也最容易踩坑的“合并”
这是你日常写 SQL 时真正会用到的集合操作。它不关心表结构是否相同,只强制要求:列数一致 + 对应列类型兼容(如 INT 和 BIGINT 可隐式转换,VARCHAR 和 TEXT 在多数场景下也可)。
-
UNION自动去重 + 默认排序(MySQL 会按第一列升序排,这不是标准行为,但当前版本确实如此) -
UNION ALL不去重、不排序,性能更高,90% 的报表拼接场景该用它 - 列名以第一个
SELECT的字段名为准;想统一命名,必须在第一个语句里用AS - 错误现象常见于:两个
SELECT列数不等 → 报错ERROR 1222 (21000): The used SELECT statements have a different number of columns
SELECT name, 'employee' AS source FROM employees UNION ALL SELECT name, 'manager' AS source FROM managers ORDER BY name;
INTERSECT 和 EXCEPT:别急着写,先看 MySQL 版本
INTERSECT 和 EXCEPT 是标准 SQL 集合操作,但 MySQL 直到 8.0.31 版本(2022 年 10 月发布)才正式支持。如果你用的是 5.7、甚至某些早期 8.0(如 8.0.23),这些关键字直接报错 ERROR 1064。
- 不要靠网上老教程试
SELECT ... INTERSECT SELECT ...,先查版本:SELECT VERSION(); - 若版本不支持,可用
INNER JOIN模拟INTERSECT,用LEFT JOIN ... WHERE right.id IS NULL模拟EXCEPT - 性能上,显式 JOIN 通常比模拟集合操作更可控,尤其涉及大表时
-- MySQL 8.0.31+ 才能跑通 SELECT id, name FROM students_2023 INTERSECT SELECT id, name FROM students_2024;
别把 GROUP BY / IN / EXISTS 当成集合操作
新手常混淆:以为 IN 子句或 GROUP BY 是在做“集合运算”。其实它们完全不是一回事:
IN (1,2,3)是值列表匹配,不是集合查询;IN (SELECT ...)是子查询,属于“标量子查询”或“行子查询”,和UNION的并集逻辑无关GROUP BY是分组聚合,输出的是汇总行,不是多个结果集的集合运算真正的集合操作,必须由两个及以上独立
SELECT语句构成,中间用UNION类操作符连接错误认知:“
WHERE status IN ('active','pending')就是集合查询” → 实际只是枚举值匹配正确判断依据:看 SQL 里有没有多个 SELECT + UNION/INTERSECT/EXCEPT 连接
为什么 ORDER BY 必须写在最后?
因为集合操作的结果是一个“新临时结果集”,ORDER BY 只能作用于最终合并后的整体,不能对单个 SELECT 单独排序(除非用括号包成子查询,但那就脱离集合操作本意了)。
- 写法错误:
SELECT a FROM t1 ORDER BY a UNION SELECT b FROM t2;
→ 语法错误 - 正确写法:
(SELECT a FROM t1) UNION (SELECT b FROM t2) ORDER BY a;
- 如果你真需要每个分支分别排序再合并,说明你其实不该用
UNION,而该用应用层拼接或临时表
实际开发中,很多人卡在这一步——不是不会写,而是没意识到 ORDER BY 的作用域限制。它不像 Python 的 sorted() 可以链式调用,它是 SQL 执行计划末端的收尾动作。
集合操作看着像数学,但 MySQL 实现得挺务实:支持什么、怎么优化、哪里会隐式转换,全得按它的规则来。别套理论,先 SELECT VERSION(),再动手。










