如何将MySQL每日加密货币价格统计插入查询转换为MongoDB聚合查询?
解决MongoDB聚合实现每日加密货币价格统计的问题
我来帮你把MySQL的统计逻辑转换成正确的MongoDB聚合管道,先分析你的需求和之前的错误,再给出完整的解决方案:
核心需求回顾
你需要按slug分组,统计当日加密货币的以下指标:
priceUsd和priceBtc的平均值priceUsd的最高价(high)和最低价(low)- 当日最早更新的
priceUsd(open)和最晚更新的priceUsd(close) - 保留
volumeUsd24h和marketcapUsd(MySQL中是取分组内任意一行的值,这里我们用$first匹配原逻辑) - 将结果写入
DemoDaily集合,按slug排序
错误分析(你的原代码问题)
- 聚合阶段顺序错误:你把
$project放在了$group之前,这会导致$avg、$max这类聚合函数无法对分组后的数据计算——$group才是分组聚合的核心阶段,必须放在统计计算之前。 - open/close字段逻辑错误:你使用了不存在的
$sortBy操作符,MongoDB无法直接通过这种方式获取排序后的首尾值,需要先全局排序再用$first/$last提取。 - $group阶段缺失聚合字段:你的
$group只指定了slug作为分组键,完全没包含需要计算的统计字段,自然无法生成所需结果。
正确的MongoDB聚合代码
// 构造当日的时间范围(替换成你的$today变量,比如new Date('2021-09-07')) const today = new Date(); // 假设当前日期为目标统计日 const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate()); const endOfDay = new Date(startOfDay); endOfDay.setDate(endOfDay.getDate() + 1); const result = await DemoHourly.aggregate([ // 1. 筛选当日的数据:匹配lastUpdated在当天00:00到次日00:00之间 { $match: { lastUpdated: { $gte: startOfDay, $lt: endOfDay } } }, // 2. 先按slug分组排序,再按lastUpdated升序排序,确保后续$first/$last能拿到正确的open/close { $sort: { slug: 1, lastUpdated: 1 } }, // 3. 按slug分组,计算所有统计指标 { $group: { _id: "$slug", // 用slug作为分组唯一标识 avgPriceUsd: { $avg: "$priceUsd" }, avgPriceBtc: { $avg: "$priceBtc" }, high: { $max: "$priceUsd" }, low: { $min: "$priceUsd" }, open: { $first: "$priceUsd" }, // 升序排序后的第一个值就是当日最早价格 close: { $last: "$priceUsd" }, // 升序排序后的最后一个值就是当日最晚价格 volumeUsd24h: { $first: "$volumeUsd24h" }, // 取分组内第一个的24h成交量(匹配MySQL逻辑) marketcapUsd: { $first: "$marketcapUsd" }, // 取分组内第一个的市值(匹配MySQL逻辑) date: { $first: startOfDay } // 设置当日起始时间作为统计日期 } }, // 4. 整理输出字段:把_id转为slug,让结构更清晰(可选) { $project: { _id: 0, slug: "$_id", avgPriceUsd: 1, avgPriceBtc: 1, volumeUsd24h: 1, marketcapUsd: 1, high: 1, low: 1, open: 1, close: 1, date: 1 } }, // 5. 按slug升序排序 { $sort: { slug: 1 } }, // 6. 将结果写入DemoDaily集合:注意$out会替换整个集合,若要增量更新可改用$merge { $out: "DemoDaily" } ]);
关键逻辑说明
- 时间范围筛选:用
$gte和$lt匹配完整的当日时间段,避免直接匹配日期字符串,确保时间精度正确。 - 排序的作用:先按
slug和lastUpdated升序排序,这样在$group阶段用$first就能拿到当日最早价格(open),$last拿到最晚价格(close),完美对应MySQL中GROUP_CONCAT取首尾的逻辑。 - $group阶段的聚合:
$avg/$max/$min直接对应MySQL的AVG()/MAX()/MIN()$first用于获取分组内的第一个值,对应MySQL中未聚合字段的取任意行逻辑
- 增量更新提示:如果不需要覆盖
DemoDaily集合,而是增量更新已有数据,可以把$out换成$merge,配置匹配条件为slug和date来更新重复数据。
内容的提问来源于stack exchange,提问作者Sahil Geek
相关产品推荐
相关产品推荐

