mysql如何使用UNION合并结果_mysql集合去重合并

5次阅读

union自动去重但性能差,需排序和临时表;若数据天然无重复应改用union all提升2–5倍性能;列数、类型须一致,隐式转换有风险;order by/limit仅限末尾;不可update/delete;去重基于整行而非单列。

mysql如何使用UNION合并结果_mysql集合去重合并

UNION 会自动去重,但代价是排序和临时表

UNION 默认行为是去重合并,本质是执行 select DISTINCT + 隐式 ORDER BYmysql 8.0.19+ 后不再强制排序,但仍需去重逻辑)。这意味着它必须把所有结果集先拉到内存或磁盘临时表里,再做唯一性判断——数据量一大就明显变慢。

常见错误现象:UNION 查询比单个子查询慢几倍,甚至触发 using temporary; Using filesort;或者在 EXPLAIN 里看到 Extra 列出现 Using temporary

  • 如果确定两个结果集天然无重复(比如查不同日期的订单、不同状态的用户),直接用 UNION ALL,性能通常能提升 2–5 倍
  • UNION 要求各子查询列数相同、对应列类型兼容;类型不一致时 MySQL 会隐式转换,可能出意外(比如 VARcharTEXT 比较时截断)
  • 列名以第一个子查询为准,后续子查询的列别名无效;想统一字段名,只能在第一个子查询里起别名

列类型不匹配时,UNION 的隐式转换很危险

MySQL 对 UNION 各子查询对应列取“最小公分母”类型:比如一个子查询是 TINYint,另一个是 INT,最终结果列类型会升为 INT;但如果一个是 VARCHAR(10),另一个是 VARCHAR(255),就会按长的那个定宽——看起来没事,但若后续接 GROUP BY 或索引字段,可能因长度超出导致无法使用索引。

使用场景:合并日志表(log_202401log_202402)时,某列在旧表是 CHAR(8),新表改成了 VARCHAR(16)UNION 后该列实际变成 VARCHAR(16),但隐式转换可能让优化器误判 selectivity。

  • SHOW CREATE table 确认各表对应列的实际类型和长度
  • 必要时手动 CAST 统一,比如 CAST(user_id AS UNSIGNED),避免依赖隐式规则
  • 特别警惕 NULL 和空字符串混用:一个子查询返回 NULL,另一个返回 ''UNION 会视为不同值,但业务上可能等价

ORDER BY 和 LIMIT 只能写在最后,不能每个子查询单独加

UNION 是集合操作,语法上只允许整个语句末尾有一个 ORDER BY 和一个 LIMIT。如果在子查询里写 ORDER BY,MySQL 会报错 this type of clause is not allowed in a UNION(除非配合 LIMIT 写成子查询嵌套)。

容易踩的坑:想“先取每个表最新 10 条再合并”,直接写 (SELECT * FROM t1 ORDER BY ts DESC LIMIT 10) UNION (SELECT * FROM t2 ORDER BY ts DESC LIMIT 10) 会失败。

  • 正确做法是把子查询包进括号,当成派生表:(SELECT * FROM t1 ORDER BY ts DESC LIMIT 10) AS a UNION (SELECT * FROM t2 ORDER BY ts DESC LIMIT 10) AS b
  • 但注意:这样写后,外层 ORDER BY 才能生效;否则合并结果顺序不可控
  • 如果真要控制每个子集的排序逻辑,得用 ROW_NUMBER() 窗口函数(MySQL 8.0+),而不是依赖 UNION 内部顺序

UNION 结果集无法直接 UPDATE 或 DELETE

MySQL 不允许对 UNION 查询结果执行 UPDATEDELETE,会报错 You can't specify target table for update in FROM clause(即使没显式写 FROM)。这不是 bug,是语法限制:UNION 返回的是虚拟结果集,没有物理表对应。

使用场景:想“找出 A 表和 B 表中 status=0 的所有 id,然后批量更新为 1”——不能写 UPDATE (SELECT id FROM a WHERE status=0 UNION SELECT id FROM b WHERE status=0) SET status = 1

  • 拆成两步:先用 UNION 查出 id 列表,再用 IN 或临时表驱动更新
  • 更稳妥的是用 INSERT ... ON DUPLICATE KEY UPDATEREPLACE INTO 配合临时表中转
  • 如果只是去重后导出数据,UNION 完全够用;但凡涉及写操作,就得绕开它

最常被忽略的一点:UNION 的去重逻辑基于整行比较,不是单列。哪怕你只关心 id 去重,它也会把 id 相同但其他列不同的行全留下——因为行不完全重复。真要按某列去重,得用 GROUP BY 或窗口函数,而不是依赖 UNION。

text=ZqhQzanResources