集合运算里的排序与限行
把多个查询结果拼在一起时,排序与限行的作用范围由位置决定:写在整条语句最后,作用于合并后的完整结果;写在某个分支内部,只作用于该分支自己。要让单个分支排序或限行,必须用括号把这个分支包起来,否则语句的解析结果与预期完全不同。这个细节在处理「每个来源取前若干条再合并」的需求时特别关键。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| 整体排序 | 写在最后,作用于合并后的结果 |
| 分支限行 | 分支用括号包裹,各自写排序与限行 |
| 列结构要求 | 各分支的列数一致、顺序对应、类型兼容 |
| 去重差异 | 保留全部用不去重形式,否则会额外做一次去重 |
典型使用场景
- 多个来源各取最新若干条再合并展示(典型的信息流场景)
- 分表数据合并统计:先合并再聚合
- 多条件筛选结果拼成一个列表,最后统一排序分页
- 跨库或跨表去重合并:需要显式指定去重形式
- 排查结果行数异常:确认是分支限行没生效还是整体排序缺失
作用范围示例
作用范围示例
-- 分支各自取最近的 5 条,最后对合并结果整体排序
(SELECT id, name FROM t1 ORDER BY id DESC LIMIT 5)
UNION ALL
(SELECT id, name FROM t2 ORDER BY id DESC LIMIT 5)
ORDER BY id; -- 这一句作用于合并后的整体
-- 分表合并后再聚合:聚合放在最外层
SELECT city, SUM(amount) AS total, COUNT(*) AS cnt
FROM (
SELECT city, amount FROM orders_2023
UNION ALL
SELECT city, amount FROM orders_2024
) t
GROUP BY city
ORDER BY total DESC;
-- 分支只写限行不写排序,取到哪几行不确定
-- SELECT id FROM t1 LIMIT 5 ← 不要这样用,先加排序位置与作用范围
| 写法位置 | 作用范围 | 注意事项 |
|---|---|---|
| 语句最后 | 合并后的整体结果 | 最外层排序决定最终顺序 |
| 分支内部(有括号) | 该分支自己的结果 | 必须用括号包住分支 |
| 分支内部(无括号) | 解析结果与预期不同 | 常见误解来源,务必加括号 |
| 分支只写限行 | 取行不确定 | 不写排序时结果不可预期 |
| 去重形式 | 合并时是否去重 | 分支之间有重复数据时影响行数 |
最外层排序会覆盖分支内部排序产生的顺序:分支排序只决定该分支贡献了哪几行,最终呈现顺序完全由最外层决定。想保留「按来源分组」的展示效果,需要在结果里额外带一个来源标记列并据此排序。
分支数量多时,各分支都写排序加限行会让优化器难以统一优化。数据量大时考虑改写成窗口函数一次性取数,通常比多个分支各自扫描更经济。