MongoDB 的聚合管道(Aggregation Pipeline)是一种链式数据处理方式,通过 $match、$group、$lookup、$unwind 等操作符组合完成复杂查询。迁移到关系型数据库后,这些聚合管道需要改写为 SQL 查询。
大部分聚合操作都有 SQL 等价写法,但有些操作在 SQL 中表达起来会更复杂,或者性能特征不同。
常见操作的 SQL 对应
$match → WHERE
最直接的映射。MongoDB 的 $match 对应 SQL 的 WHERE 子句。
注意:MongoDB 的查询条件语法和 SQL 不同,比如等于操作在 MongoDB 中是 {field: value},SQL 中是 field = value。范围查询 MongoDB 用 $gt/$lt,SQL 用 >/<。
$group → GROUP BY + 聚合函数
MongoDB 的 $group 对应 SQL 的 GROUP BY。常用聚合函数的映射:
- $sum → SUM()
- $avg → AVG()
- $max / $min → MAX() / MIN()
- $first / $last → 没有直接等价物,需要用子查询或窗口函数
$sort → ORDER BY
直接对应。但需要注意:不显式加 ORDER BY 就不要依赖返回顺序,MongoDB 和 SQL 在这方面都一样,不同数据库对相同键值行的排序稳定性也不一定保证。
$limit / $skip → LIMIT / OFFSET
直接对应。但要注意 $skip + $limit 在大数据量下性能差(MongoDB 需要扫描跳过的文档),SQL 的 OFFSET 也有同样的问题。
$project → SELECT
MongoDB 的 $project 选择和重命名字段,对应 SQL 的 SELECT。但 $project 还支持表达式计算,这对应 SQL 中的计算列。
需要重点关注的操作
$lookup → JOIN
MongoDB 的 $lookup 实现左外连接,对应 SQL 的 LEFT JOIN。需要注意:
- MongoDB 的 $lookup 默认是等值连接,SQL 的 JOIN 支持更多条件类型
- $lookup 可以嵌套在管道中间,SQL 的 JOIN 通常在查询的开头定义
- 多个 $lookup 在 SQL 中对应多个 JOIN,需要注意 JOIN 顺序对性能的影响
$unwind → 关联表展开
MongoDB 的 $unwind 把数组拆成多行文档。如果迁移时数组已经拆为关联表,$unwind 对应的就是对这个关联表的 JOIN 操作,不需要特殊处理。
$facet → 多查询并行
$facet 允许在同一管道中对同一输入数据执行多个聚合操作。SQL 中没有直接等价物,需要拆分为多个独立查询,或者使用 CTE(Common Table Expression)来组织。
$bucket / $bucketAuto → CASE WHEN + GROUP BY
分桶聚合在 SQL 中用 CASE WHEN 表达式实现分组条件,再 GROUP BY。
性能差异
MongoDB 聚合管道和 SQL 查询的执行方式不同:
- MongoDB 的聚合管道是流式处理,每个阶段处理完就传给下一阶段
- SQL 查询由优化器生成执行计划,可能重排操作顺序
- 复杂聚合管道(多层 $lookup、$unwind)在 SQL 中可能产生复杂的执行计划,需要关注 EXPLAIN 的输出
实际建议
- 先把 MongoDB 的聚合管道按阶段拆解,理解每个阶段的语义
- 逐阶段翻译为 SQL 子查询或 CTE
- 用 EXPLAIN 验证 SQL 的执行计划是否合理
- 关注 MongoDB 和 SQL 在 NULL 处理上的差异(MongoDB 中字段不存在和值为 null 是不同的,SQL 中通常统一为 NULL)
TiDB 在 MongoDB 聚合管道迁移中的对应能力
TiDB 兼容 MySQL 协议,支持完整的 SQL 语法包括 CTE、窗口函数和 JOIN,聚合管道中的大部分操作都可以用标准 SQL 表达。TiDB 的查询优化器会自动选择执行计划,对于复杂的聚合查询可以利用 TiFlash 列存节点加速。建议在迁移前将高频聚合管道逐条翻译为 SQL,用 EXPLAIN 验证执行计划,确认性能可接受。
如果你正在规划 MongoDB 到关系型数据库的迁移,建议先梳理所有聚合管道的复杂度,重点标注包含 $lookup、$facet 的管道,再做一轮 SQL 翻译和性能验证。