讲解

UNION 把两个(或多个)SELECT 的结果纵向拼接成一个结果集:上面的行加下面的行。两个查询的列数必须相同,对应位置的类型要兼容,结果的列名取自第一个查询。默认的 UNION 会自动去重——完全相同的行只保留一份;想保留全部行(包括重复)用 UNION ALL。

UNION 和 UNION ALL 怎么选?当你明知两份结果不可能重复、或重复正是你要的,用 UNION ALL:它省去去重的排序开销,语义也更诚实。只有确实需要去重时才用 UNION。实际开发中 UNION ALL 更常见,比如把「学生表」和「课程表」里各自的名称合成一张名录这种跨表合并。

排序规则也要记住:ORDER BY 只能写在最后一个 SELECT 之后,对整个合并结果排序,不能给每个分支单独排序。如果业务上需要「每段内部有序」,得给每段加一个人工的排序键列再统一排序。列名取自第一段,所以别名要在第一个 SELECT 里起好。

示例

UNION ALL 合并两段统计:已支付订单的总额、待支付订单的总额,作为两行报表:

SELECT '已支付' AS label, COUNT(*) AS cnt, SUM(amount) AS total FROM orders WHERE status = 'paid'
UNION ALL
SELECT '待支付', COUNT(*), SUM(amount) FROM orders WHERE status = 'pending'
ORDER BY total DESC;

UNION 的去重效果:把两个城市清单合并,重复值只留一份:

SELECT city FROM students WHERE age >= 20
UNION
SELECT city FROM students WHERE class_name = '二班'
ORDER BY city;

跨表名录:学生姓名和课程标题合成一个「名称列表」,用类型列区分来源:

SELECT name AS item, '学生' AS kind FROM students
UNION ALL
SELECT title, '课程' FROM courses
ORDER BY kind, item;

常见坑

  • 两段列数或类型不匹配:列数不同直接报错;类型不兼容时各数据库处理不同(SQLite 宽容,PostgreSQL 严格)。合并前先确认两段 SELECT 的列结构一致。
  • 用 UNION 而忘了它去重:两段各 100 行合并后只剩 150 行,多半是 UNION 悄悄去掉了重复。不想丢行就写 UNION ALL。
  • 在分支里写 ORDER BY:ORDER BY 只对最终结果生效一次,写在中间分支里是语法错误(或需要用括号加 LIMIT 的特殊技巧,可读性差,别这么干)。
  • 列名在第二个 SELECT 里起别名:结果列名永远来自第一段,后面的别名被忽略。别名统一在第一段写。

小结

UNION 纵向合并结果集并去重,UNION ALL 保留全部行;列数和类型要对齐,ORDER BY 只能放最后。下一节学习在查询里做分支判断:CASE WHEN。