mysql如何使用union合并查询_mysql联合查询技巧

6次阅读

union 默认去重并隐式排序,需用 union all 保留重复行且提升性能;列数、类型须严格一致,排序和 limit 只能作用于最终结果。

mysql如何使用union合并查询_mysql联合查询技巧

UNION 会自动去重,要保留重复行得用 UNION ALL

很多人写 UNION 是为了拼接两个结果集,但发现最终行数变少了——这是因为 UNION 默认执行去重(等价于 UNION DISTINCT)。如果两个查询里有相同结构的重复记录,它们会被合并成一条。

真正需要“叠加”数据时(比如日志表分月归档、多租户数据汇总),必须显式写成 UNION ALL。它不比较重复、不排序、不建临时表,性能也明显更好。

  • UNION:隐式加 DISTINCT + 隐式 ORDER BY(按第一列升序),实际执行会建临时表并排序
  • UNION ALL:纯追加,零额外开销,适合大数据量拼接
  • mysql 8.0+ 对 UNION 的去重逻辑更严格,遇到 NULL 或浮点精度差异也可能被判定为“不同”,但行为仍不如 UNION ALL 可控

列数、类型和顺序必须严格一致,否则报错 1222

执行 UNION 时 MySQL 不看字段名,只校验「列数」和「对应位置的表达式类型兼容性」。常见报错 Error 1222 (21S01): The used select statements have a different number of columns 就是列数不匹配。

类型不兼容也会出问题:比如左边是 int,右边是 VARCHAR,MySQL 会尝试隐式转换,但若转换失败(如 'abc'INT)或精度丢失(如 DECIMAL(10,2)DECIMAL(5,0) 拼接),可能返回截断值或警告。

  • 所有 SELECT 子句必须有相同数量的列,不能靠 SELECT * 蒙混——表结构稍有变动就崩
  • 建议显式写出字段,用 CAST()CONVERT() 统一类型,例如:CAST(create_time AS date)
  • 列别名以第一个 SELECT 的为准,后续子句的别名会被忽略

排序和限制只能加在最后,不能每个子句单独加 ORDER BY / LIMIT

UNION 是集合操作,整个结果是一个新结果集。你不能在每个 SELECT 后面加 ORDER BYLIMIT,MySQL 会直接报语法错误(除非用括号包成派生表)。

如果真要对某一部分先取 top N 再合并,得用子查询包装:(SELECT ... ORDER BY x LIMIT 10) UNION ALL (SELECT ... ORDER BY y LIMIT 10)。但注意:子查询里的 ORDER BY 在没有 LIMIT 时无效,MySQL 5.7+ 会警告;有 LIMIT 才真正生效。

  • 全局排序必须放在整个 UNION 语句末尾,例如:... UNION ALL ... ORDER BY id DESC
  • 想按不同逻辑分别排序再合并?不行。得用应用层处理,或改用 JOIN + 条件标记
  • LIMIT 放在末尾只限制最终结果行数,不是每个分支的限制

性能差?检查是否误用了 UNION 而非 JOIN,或没加索引

UNION 本身不慢,慢往往是因为:① 用了 UNION 去替代本该用 JOIN 的关联场景;② 每个子查询都没走索引,导致全表扫描多次;③ UNION 后还要 ORDER BY + LIMIT,触发文件排序(using filesort)甚至磁盘临时表。

EXPLAIN 看执行计划时,每个 SELECT 都会单独出现一行,type 为 ALL 就说明没走索引;Extra 出现 Using temporary; Using filesort 就是性能瓶颈信号。

  • 两个表有主外键关系?优先考虑 LEFT JOIN,不是 UNION
  • 子查询涉及大表,确保 WHERE 条件字段上有索引,尤其注意函数索引失效问题(如 YEAR(create_time) = 2024
  • MySQL 8.0+ 支持 CTE,复杂拼接可先用 WITH 定义中间结果,提升可读性和优化器判断能力

UNION 的边界很清晰:它只负责横向拼接,不关联、不过滤、不计算关系。一旦开始纠结“怎么让左边排好序再和右边拼”“为什么 LIMIT 不生效”,基本说明用错了场景。

text=ZqhQzanResources