Prisma中按日期分组并计算总和的完整实现指南

2025年12月19日/ 浏览 21

正文:

在数据分析和业务统计场景中,按日期分组并计算总和是一项高频需求。例如统计每日销售额、用户活跃数等。Prisma 作为现代 Node.js 的 ORM 工具,虽然官方未直接提供 GROUP BY 语法,但通过灵活组合查询仍能高效实现这一功能。以下是具体实现方法及实战技巧。


1. 原生 SQL 查询方案

当需要复杂分组时,可直接使用 Prisma 的 $queryRaw 执行原生 SQL。例如统计 orders 表每日订单总额:


const result = await prisma.$queryRaw`
  SELECT 
    DATE(createdAt) as date,
    SUM(amount) as total
  FROM Order
  GROUP BY DATE(createdAt)
`;

优点
– 直接利用数据库的聚合能力,性能最优
– 支持复杂日期格式化(如按年、月分组)

缺点
– 需手动处理 SQL 注入风险
– 返回结果类型需额外定义


2. 纯 Prisma 客户端方案

如果希望避免原生 SQL,可通过以下步骤实现:

步骤 1:查询原始数据


const orders = await prisma.order.findMany({
  select: {
    amount: true,
    createdAt: true
  }
});

步骤 2:内存中分组计算


import { groupBy, sum } from 'lodash';

const grouped = groupBy(orders, order => 
  order.createdAt.toISOString().split('T')[0] // 按YYYY-MM-DD分组
);

const dailyTotals = Object.entries(grouped).map(([date, items]) => ({
  date,
  total: sum(items.map(i => i.amount))
}));

适用场景
– 数据量较小(万条以内)
– 需跨多表关联计算时


3. 混合方案(推荐)

结合 Prisma 的聚合 API 与内存处理,平衡安全性与灵活性:


const rawData = await prisma.order.groupBy({
  by: ['createdAt'],
  _sum: { amount: true },
  orderBy: { createdAt: 'asc' }
});

// 格式化日期并二次聚合
const result = rawData.reduce((acc, item) => {
  const dateKey = item.createdAt.toISOString().split('T')[0];
  acc[dateKey] = (acc[dateKey] || 0) + item._sum.amount;
  return acc;
}, {});

性能优化建议

  1. 索引优化:为 createdAt 字段添加数据库索引
  2. 分批处理:大数据集时使用 skip/take 分页
  3. 缓存结果:对统计结果使用 Redis 缓存

通过上述方法,开发者可根据实际场景选择最适合的解决方案,高效实现日期分组统计需求。

picture loss