【问题标题】:Linq with join, sum and group by C#使用 C# 进行连接、求和和分组的 Linq
【发布时间】:2020-01-24 02:39:06
【问题描述】:

我在下面给出了两个表格: 表 01:收入。

Id  |  Amount |  Date       | UserId
1   |  50000  | 2019-01-10  |   1
1   |  10000  | 2019-01-15  |   1
1   |  15000  | 2019-02-16  |   1
1   |  20000  | 2019-03-01  |   1

表 02:费用。

Id  |  Amount |  Date       | UserId
1   |  10000  | 2019-03-24  |   1
1   |   5000  | 2019-03-24  |   1
1   |  30000  | 2019-04-24  |   1
1   |  20000  | 2019-05-24  |   1

我想把每个月的收入和支出放在一起。 实际上我想要的结果是这样的(基于上表):

Month  |  Income  |  Expenses
   01  |  60000   |     0
   02  |  15000   |     0
   03  |  20000   |   15000
   04  |    0     |   30000
   05  |    0     |   20000

到目前为止我所尝试的:

var incomes = await _context.Incomes.Where(i => i.Date.Year == year && i.UserId == id && i.IsApproved).ToListAsync();
var expenses = await _context.Expenses.Where(e => e.Date.Year == year && e.UserId == id && e.IsApproved).ToListAsync();

var yearlyIncomes = incomes.GroupBy(
            i => i.Date.Month, 
            i => i.Amount,
            (month, income) => new { Month = month, Income = income.Sum() }).OrderBy(i => i.Month).ToList();

var yearlyExpenses = expenses.GroupBy(
            e => e.Date.Month, 
            e => e.Amount,
            (month, expense) => new { Month = month, Expense = expense.Sum() }).OrderBy(e => e.Month).ToList();

这样,我可以单独得到每个月的收入和支出。

我也试过了:

var yearlyIncomes = incomes.GroupBy(x => x.Date.Month)
                        .Select(x => new
                        {
                            Month = x.Key,
                            Income = x.Sum(p => p.Amount),
                            Expense = expenses.Where(c => x.Any(p => p.Date.Month == c.Date.Month)).Sum(c => c.Amount)
                        }).ToList();

有什么方法可以通过一个查询获得所需的结果。

提前致谢。

【问题讨论】:

  • 第二个查询效率不高,因为它将尝试每月获取一次费用。您的第一种方法看起来不错(有时越多越好)。您可以做的一件事是在用户 ID 上加入收入和支出,然后按月进行分组以获得收入和支出。此外,您的分组方式看起来很奇怪,您应该按月分组并汇总收入/费用。
  • 使用异步调用,您无需在一个查询中完成。通过在不等待的情况下调用它们来“同时”执行这两个查询。使用Task.WhenAll 等待两个查询,然后计算所需的结果。
  • @ArfizurRahman 希望你能试试我的回答
  • @GabrielLlorico 感谢您的回答。我试过了,但它不工作。我得到的不是我想要的结果。例如:如果您查看我的问题中的示例输出表,则第三个月的收入和支出只有一行,但根据您的解决方案,我在第 3 个月得到两行。即收入和支出应该在同一行。谢谢
  • @ArfizurRahman,我明白了,我忘了重新组合,等待,更新我的答案

标签: c# linq join group-by sum


【解决方案1】:

如果您不想按UserId 对其进行分组,请尝试此操作,您可以在一个 linq 中完成,但会很长

var groupedIncome = incomes
                 .GroupBy(inc => inc.Date.Month) //group it by month
                 .Select(groupedIncomeByMonth => 
                     new { 
                         Month = groupedIncomeByMonth.Key, 
                         Amount = groupedIncomeByMonth
                                      .Sum(monthIncome => monthIncome.Amount), 
                         IsIncome = true}).ToList(); // Marker for Concat/Union All

var groupedExpenses = expenses
                    .GroupBy(exp => exp.Date.Month)
                    .Select(groupedExpenseByMonth => 
                        new { 
                            Month = groupedExpenseByMonth.Key, 
                            Amount = groupedExpenseByMonth
                                         .Sum(monthExpense => monthExpense.Amount), 
                            IsIncome = false }).ToList(); // marker for Concat/Union All

var result = groupedIncome.Concat(groupedExpenses)
                  .GroupBy(x => x.Month)
                  .Select(r => 
                               new { 
                                       Month = r.Key, 
                                       Income = r.Where(p => p.IsIncome).Sum(g => g.Amount), 
                                       Expense = r.Where(p => !p.IsIncome).Sum(g => g.Amount)
                                   }).ToListAsync();

【讨论】:

  • 尝试了您更新的答案。引发此异常:System.ArgumentException:方法 'System.Collections.Generic.IAsyncEnumerable1[System.Object] Concat[Object](System.Collections.Generic.IAsyncEnumerable1[System.Object] 的“System.Collections.Generic.IAsyncEnumerable1[Microsoft.EntityFrameworkCore.Storage.ValueBuffer]' cannot be used for parameter of type 'System.Collections.Generic.IAsyncEnumerable1[System.Object]”类型表达式.Collections.Generic.IAsyncEnumerable`1[System.Object])'
  • @ArfizurRahman 嗯,我认为你应该在内存中执行这些
  • @GrabrielLlorico 我必须将 ToListAsync() 更改为 ToList() 才能使其正常工作。感谢您的回答与合作。
【解决方案2】:

您想在通过调用ToListAsync() 方法实现它们之前合并所有数据并将它们分组。

var incomes = _context
    .Incomes
    .Where(i => i.Date.Year == year && i.UserId == id && i.IsApproved)
    .Select(i => new { Month = i.Date.Month, Income = i.Amount, Expense = 0 });

var expenses = _context
    .Expenses
    .Where(e => e.Date.Year == year && e.UserId == id && e.IsApproved)
    .Select(e => new { Month = e.Date.Month, Income = 0, Expense = e.Amount });

var summary = await incomes
    .Concat(expenses)
    .GroupBy(s => s.Month)
    .Select(g => new { Month = g.Key, Incomes = g.Sum(x => x.Income), Expenses = g.Sum(x => x.Expense) })
    .ToListAsync();

【讨论】:

  • Union 消除了重复(即同一个月的收入相同)。正确的运算符应该是 Concat imho。
  • @AntonínLejsek:哦,是的。康卡特,联合所有!谢谢!
  • @dropoutcoder 感谢您的回答。尝试使用 Concat 解决方案,但会引发异常: System.ArgumentException: Expression of type 'System.Collections.Generic.IAsyncEnumerable1[Microsoft.EntityFrameworkCore.Storage.ValueBuffer]' cannot be used for parameter of type 'System.Collections.Generic.IAsyncEnumerable1[System.Object]' of method 'System.Collections.Generic.IAsyncEnumerable1[System.Object] Concat[Object](System.Collections.Generic.IAsyncEnumerable1' [System.Object]、System.Collections.Generic.IAsyncEnumerable`1[System.Object])'。未能找到任何解决方案。有什么想法吗?
猜你喜欢
  • 2016-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-29
  • 1970-01-01
  • 1970-01-01
  • 2021-06-10
  • 2023-04-02
相关资源
最近更新 更多