如何在ASP.NET Core中按月查询销售数据并存储到PostgreSQL?
解决方案:ASP.NET Core + PostgreSQL 实现月度销售统计并存储
一、问题分析
你的现有代码错误地按销售记录ID统计次数(每条记录ID唯一,统计结果无实际意义),需要改为按销售年月分组统计总销量,同时需创建专门的月度报表表存储聚合后的统计数据(而非更新单条销售记录的字段)。
二、步骤1:定义月度报表实体与数据库上下文
首先创建存储月度统计的实体类,并将其添加到DbContext:
// 月度销售报表实体 public class MonthlySalesReport { public int Id { get; set; } public int Year { get; set; } public int Month { get; set; } public int TotalSales { get; set; } // 可选:添加唯一约束,避免重复插入同一年月的记录 [Index(nameof(Year), nameof(Month), IsUnique = true)] public string YearMonthKey => $"{Year}{Month:D2}"; } // 在你的DbContext中添加DbSet public DbSet<MonthlySalesReport> MonthlySalesReports { get; set; }
执行数据库迁移创建对应表:
dotnet ef migrations add AddMonthlySalesReport dotnet ef database update
三、步骤2:优化查询逻辑(数据库端分组统计)
不要拉取所有数据到内存统计,直接用EF Core的Linq查询在PostgreSQL端完成分组,提升性能:
// 从数据库查询并分组统计月度销量(过滤已删除记录) var monthlySalesStats = Repository.repoSales .Where(s => !s.IsDeleted) .GroupBy(s => new { s.SaleDate.Year, s.SaleDate.Month }) .Select(g => new { Year = g.Key.Year, Month = g.Key.Month, TotalSales = g.Count() }) .ToList();
四、步骤3:将统计结果保存到数据库
采用"存在则更新,不存在则插入"的逻辑,避免重复数据:
foreach (var stat in monthlySalesStats) { // 检查该年月的报表是否已存在 var existingReport = Repository.repoMonthlySalesReports .FirstOrDefault(r => r.Year == stat.Year && r.Month == stat.Month); if (existingReport != null) { // 更新现有记录 existingReport.TotalSales = stat.TotalSales; Repository.repoMonthlySalesReports.Update(existingReport); } else { // 创建新记录 var newReport = new MonthlySalesReport { Year = stat.Year, Month = stat.Month, TotalSales = stat.TotalSales }; Repository.repoMonthlySalesReports.Add(newReport); } } // 统一提交所有修改到数据库 Repository.SaveChanges();
五、步骤4:输出期望的统计结果
格式化年月为MM/yyyy格式并输出:
foreach (var stat in monthlySalesStats) { Console.WriteLine($"{stat.Month:D2}/{stat.Year} - {stat.TotalSales}"); }
六、完整代码示例
整合后的控制台应用代码:
// 统计月度销量 var monthlySalesStats = Repository.repoSales .Where(s => !s.IsDeleted) .GroupBy(s => new { s.SaleDate.Year, s.SaleDate.Month }) .Select(g => new { Year = g.Key.Year, Month = g.Key.Month, TotalSales = g.Count() }) .ToList(); // 保存到数据库 foreach (var stat in monthlySalesStats) { var existingReport = Repository.repoMonthlySalesReports .FirstOrDefault(r => r.Year == stat.Year && r.Month == stat.Month); if (existingReport != null) { existingReport.TotalSales = stat.TotalSales; Repository.repoMonthlySalesReports.Update(existingReport); } else { var newReport = new MonthlySalesReport { Year = stat.Year, Month = stat.Month, TotalSales = stat.TotalSales }; Repository.repoMonthlySalesReports.Add(newReport); } } Repository.SaveChanges(); // 输出结果 foreach (var stat in monthlySalesStats) { Console.WriteLine($"{stat.Month:D2}/{stat.Year} - {stat.TotalSales}"); }
关键说明
- 避免内存统计:直接通过EF Core将分组逻辑推送到PostgreSQL执行,减少内存消耗与网络传输量。
- 数据唯一性:通过实体的唯一约束确保同一年月的报表只存一份,避免重复统计。
- 独立存储统计数据:月度统计是聚合数据,应存储在专门的报表表中,而非修改原始销售记录的字段。
内容的提问来源于stack exchange,提问作者Vayne
相关产品推荐
相关产品推荐

