不能。MySQL中UNION的LIMIT必须置于整个语句末尾,作用于最终合并结果,不可对各子查询单独使用;正确做法是将LIMIT放在最外层,并配合OFFSET或游标分页优化性能。

UNION 结果集能直接用 LIMIT 分页吗
不能。MySQL 中 UNION 默认返回的是一个完整结果集,LIMIT 必须放在整个 UNION 语句末尾,且只作用于最终合并后的结果,不能对每个子查询单独分页。
常见错误是写成这样(语法报错):
SELECT id, name FROM t1 LIMIT 10 UNION SELECT id, name FROM t2 LIMIT 10;
正确写法必须把 LIMIT 放在最后,并配合 OFFSET 实现分页:
(SELECT id, name, 't1' AS source FROM t1) UNION ALL (SELECT id, name, 't2' AS source FROM t2) ORDER BY id LIMIT 20 OFFSET 40;
-
UNION ALL比UNION快,除非真需要去重,否则优先用ALL -
ORDER BY必须出现在最外层,子查询里加无效(除非配合LIMIT做“取 top N”逻辑) -
OFFSET越大,性能越差——MySQL 仍需扫描前OFFSET + LIMIT行
大数据量 UNION 分页怎么避免深分页性能问题
当 OFFSET 达到几十万时,UNION 后的排序+跳过操作会非常慢。根本原因是 MySQL 无法利用索引跳过中间数据。
更可行的替代方案是「游标分页(cursor-based pagination)」:
(SELECT id, name, 't1' AS source FROM t1 WHERE id > 1000) UNION ALL (SELECT id, name, 't2' AS source FROM t2 WHERE id > 1000) ORDER BY id LIMIT 50;
- 用上一页最后一条记录的
id(或其他有序、唯一、有索引的字段)作为下一页的WHERE条件 - 必须确保
ORDER BY字段有索引,且所有UNION子查询都能用上该索引 - 多个表字段名或类型不一致时,要显式别名 + 类型对齐,否则
UNION会隐式转换导致索引失效
想对每个子查询单独分页再合并,怎么办
MySQL 不支持直接对每个 SELECT 单独 LIMIT 后再 UNION,但可以用派生表(subquery)绕过语法限制:
(SELECT * FROM (SELECT id, name FROM t1 ORDER BY id LIMIT 20) AS t1_paged) UNION ALL (SELECT * FROM (SELECT id, name FROM t2 ORDER BY id LIMIT 20) AS t2_paged) ORDER BY id LIMIT 50;
- 每个子查询先用派生表完成自己的分页和排序,再合并
- 注意:这得到的是「各表取前 N 条后合并再全局排序」,不是「全量合并后取前 N 条」,语义不同
- 如果业务允许,这种做法反而更可控——比如「每类数据展示 5 条」的运营需求
- 派生表会强制物化,可能增加临时表开销,
EXPLAIN看type=derived就说明触发了
结果集字段不一致时 UNION 容易出什么错
字段数、类型、顺序不一致会导致静默截断、隐式转换或报错,典型表现是查出数据但值不对,或者报 ERROR 1222 (21000): The used SELECT statements have a different number of columns。
- 务必保证每个
SELECT的列数完全相同,用NULL或默认值占位 - 避免混用
VARCHAR和TEXT,MySQL 会按最长宽度对齐,可能引发截断或排序异常 - 字符集不一致(如
utf8mb3vsutf8mb4)会导致UNION失败,错误类似Illegal mix of collations - 别名只对外层可见,子查询里的
AS不影响UNION列匹配,匹配只看位置
实际用的时候,先想清楚:你要的是「从全部结果里取第几页」,还是「每个来源取几条拼一起」。前者小心 OFFSET 性能,后者注意派生表开销和字段对齐。类型和字符集这些细节,线上出过问题才记得住。










