如何优化多时间维度API循环逻辑?解决重复循环性能问题
性能优化方案:批量聚合替代循环查询
当前API因大量循环导致运行缓慢,目前仅完成年度数据的图表计算逻辑,后续还需复用类似代码处理日、月、周维度的数据,重复循环会进一步降低运行效率。原年度图表计算代码如下:
// GRAPH CALCULATION FOR YEAR let query = ''; graph_data = ''; let startOfYear = MOMENT().startOf('year'); let monthsForYear = []; let year = []; // Create an array of dates representing the start of each month in the year for (let index = 0; index <= 11; index++) { const add1Month = MOMENT(startOfYear) .add(index, 'month') .format('YYYY-MM-DDTHH:mm:SS.000+00:00'); monthsForYear.push(add1Month); } // Get the actual amount for each month for (let i = 0; i < monthsForYear.length; i++) { let j = i + 1; let d = await PRISMA.orders.findMany({ where: { created_at: { gte: monthsForYear[i], lte: i === monthsForYear.length - 1 ? endOftheYear_date : monthsForYear[j], }, }, select: { actual_amount: true }, }); // Calculate the total actual amount for the month let total = 0; d.forEach((el) => { total += el.actual_amount; }); year.push(total); } // Set the graph data to the calculated amounts graphDataOfYear = year;
核心优化思路
- 数据库聚合替代循环查询:将原本N次的数据库查询合并为1次,利用Prisma的
groupBy和sum聚合函数直接在数据库层面完成分组求和,大幅减少网络IO开销。 - 移除手动日期区间生成:通过Prisma的
dateTrunc函数自动按时间维度(月/日/周/年)截断日期,无需手动生成每个区间的起止时间。 - 通用化多维度处理:封装通用函数,仅需传入时间维度参数即可适配所有统计场景,避免重复编写循环逻辑。
优化后的代码示例
年度数据计算优化版
const getYearlyGraphData = async () => { const startOfYear = MOMENT().startOf('year').toISOString(); const endOfYear = MOMENT().endOf('year').toISOString(); // 一次查询完成所有月份的总额计算 const monthlyTotals = await PRISMA.orders.groupBy({ by: ['created_at'], where: { created_at: { gte: startOfYear, lte: endOfYear } }, _sum: { actual_amount: true }, orderBy: { created_at: 'asc' } }); // 按月份整理数据,确保每个月份都有值(即使为0) const graphDataOfYear = Array(12).fill(0); monthlyTotals.forEach(item => { const monthIndex = MOMENT(item.created_at).month(); graphDataOfYear[monthIndex] = item._sum.actual_amount || 0; }); return graphDataOfYear; };
通用多维度处理函数
const getGraphDataByDimension = async (dimension) => { const validDimensions = ['year', 'month', 'week', 'day']; if (!validDimensions.includes(dimension)) throw new Error('无效的时间维度'); const startDate = MOMENT().startOf(dimension).toISOString(); const endDate = MOMENT().endOf(dimension).toISOString(); let groupByField; let totalSlots; // 根据维度配置分组字段和总槽数 switch(dimension) { case 'year': groupByField = { created_at: { dateTrunc: 'month' } }; totalSlots = 12; break; case 'month': groupByField = { created_at: { dateTrunc: 'day' } }; totalSlots = MOMENT().daysInMonth(); break; case 'week': groupByField = { created_at: { dateTrunc: 'day' } }; totalSlots = 7; break; case 'day': groupByField = { created_at: { dateTrunc: 'hour' } }; totalSlots = 24; break; } // 数据库聚合查询 const aggregatedData = await PRISMA.orders.groupBy({ by: [groupByField], where: { created_at: { gte: startDate, lte: endDate } }, _sum: { actual_amount: true }, orderBy: { created_at: 'asc' } }); // 填充数据到对应槽位 const graphData = Array(totalSlots).fill(0); aggregatedData.forEach(item => { const date = MOMENT(item.created_at); let slotIndex; switch(dimension) { case 'year': slotIndex = date.month(); break; case 'month': slotIndex = date.date() - 1; break; case 'week': slotIndex = date.day(); break; case 'day': slotIndex = date.hour(); break; } graphData[slotIndex] = item._sum.actual_amount || 0; }); return graphData; }; // 使用示例:获取月度数据 // const monthlyGraphData = await getGraphDataByDimension('month');
优化效果说明
- 查询次数从N次降为1次:原代码按时间单位循环查询多次,优化后仅需1次数据库请求,减少了大量网络往返时间。
- 计算效率提升:数据库的聚合计算比服务端循环求和效率更高,尤其是数据量较大时差异明显。
- 代码复用性增强:通用函数支持多维度统计,后续新增维度无需重复编写循环和查询逻辑,维护成本更低。
内容的提问来源于stack exchange,提问作者samtuner
相关产品推荐
相关产品推荐

