$unionwith不能直接替代sql的union all,因其默认去重且不保证顺序;需显式指定空pipeline并加$sort才能模拟union all语义,且仅mongodb 5.0+支持该用法。

为什么 $unionWith 不能直接替代 SQL 的 UNION ALL
因为 $unionWith 默认不保留重复文档,也不保证顺序——它底层是先去重再合并(类似 UNION 而非 UNION ALL)。如果你需要真正“拼接所有文档、不丢数据、不重排”,必须显式禁用去重并控制排序。
如何让 $unionWith 行为接近 UNION ALL
关键在两个地方:加 pipeline 参数避免隐式去重,再用 $sort 固定顺序。MongoDB 5.0+ 才支持 $unionWith 的 pipeline 选项,低于该版本只能靠应用层拼接或聚合 + $facet 模拟。
- 必须指定
pipeline: [](空数组),否则服务器可能自动注入$group导致去重 - 如果两集合字段不一致,
$unionWith不报错但结果字段会缺失——建议提前用$project对齐结构 - 顺序不可靠:即使源集合有序,
$unionWith合并后顺序随机,必须加$sort显式排序
示例(合并 users 和 admins,按 createdAt 时间升序):
[
{ $unionWith: { coll: "admins", pipeline: [] } },
{ $sort: { createdAt: 1 } }
]
遇到 “unrecognized field 'pipeline'” 错误怎么办
这是 MongoDB 版本太低的典型提示。检查版本:db.version()。只有 5.0 及以上才支持 $unionWith 的 pipeline 参数。4.4 及以下只能用变通方式:
- 应用层两次
find()再合并数组(适合小数据量) - 用
$facet分别查两个集合,再$unwind+$concatArrays(性能差,内存占用高) - 建视图(
db.createView)把两个集合 union 成一个逻辑集合,但视图不支持写入且无法传参
性能和内存要注意什么
$unionWith 是管道阶段,不走索引合并;若源集合很大,整个过程会在内存中完成合并与排序,容易触发 errmsg: "Sort exceeded memory limit"。
- 务必对
$sort字段建索引(如{ createdAt: 1 }),否则排序强制内存执行 - 避免在
$unionWith后立即$group或$lookup,这会让中间结果膨胀 - 如果只是分页查前 N 条,优先考虑分别查询 + 应用层归并,比全量 union 再 skip/limit 更高效
真正需要 UNION ALL 语义时,别迷信单条聚合命令——有时两条独立查询 + 合并更稳、更快、更容易调试。











