在一个 LINQ 查询中获取两列的总和

Posted

技术标签:

【中文标题】在一个 LINQ 查询中获取两列的总和【英文标题】:Get sum of two columns in one LINQ query 【发布时间】:2011-01-26 19:10:41 【问题描述】:

假设我有一张名为 项目(ID int、Done int、Total int)

我可以通过两个查询来做到这一点:

int total = m.Items.Sum(p=>p.Total)
int done = m.Items.Sum(p=>p.Done)

但我想在一个查询中完成,如下所示:

var x = from p in m.Items select new  Sum(p.Total), Sum(p.Done);

肯定有一种方法可以从 LINQ 语法中调用聚合函数...?

【问题讨论】:

【参考方案1】:

使用C# 7.0 中引入的元组语言支持,您可以使用以下 LINQ 表达式解决此问题:

var itemSums = m.Items.Aggregate((Total: 0, Done: 0), (sums, item) => (sums.Total + item.Total, sums.Done + item.Done));

完整代码示例:

var m = new

    Items = new[]
    
        new  Total = 10, Done = 1 ,
        new  Total = 10, Done = 1 ,
        new  Total = 10, Done = 1 ,
        new  Total = 10, Done = 1 ,
        new  Total = 10, Done = 1 ,
    ,
;

var itemSums = m.Items.Aggregate((Total: 0, Done: 0), (sums, item) => (sums.Total + item.Total, sums.Done + item.Done));

Console.WriteLine($"Sum of Total: itemSums.Total, Sum of Done: itemSums.Done");

【讨论】:

【参考方案2】:

这已经得到回答,但其他答案仍会在集合上进行多次迭代(多次调用 Sum)或创建许多中间对象/元组,这可能很好,但如果不是,那么你可以创建一个(或多个)扩展方法,以老式方式执行此操作,但非常适合 LINQ 表达式。

这样的扩展方法如下所示:

public static Tuple<int, int> Sum<T>(this IEnumerable<T> collection, Func<T, int> selector1, Func<T, int> selector2)

    int a = 0;
    int b = 0;

    foreach(var i in collection)
    
        a += selector1(i);
        b += selector2(i);
    

    return Tuple.Create(a, b);

你可以这样使用它:

public class Stuff

    public int X;
    public int Y;


//...

var stuffs = new List<Stuff>()

    new Stuff  X = 1, Y = 10 , 
    new Stuff  X = 1, Y = 10 
;

var sums = stuffs.Sum(s => s.X, s => s.Y);

【讨论】:

【参考方案3】:

这样就可以了:

from p in m.Items
group p by 1 into g
select new

    SumTotal = g.Sum(x => x.Total), 
    SumDone = g.Sum(x => x.Done) 
;

【讨论】:

或者当Item没有唯一标识符时,你可以写group p by p into g 这成功了,尽管做了一些修改:从 p in m.Items group p by p.Id into g select new SumTotal = g.Sum(r => r.Total), SumDone = g.Sum(r => r.Done) @vcRobe:您真的遇到给定方法不合适的问题吗?我认为使用 LINQ 没有简单的方法可以做到这一点。您可能必须遍历项目并将临时结果存储在 Dictionary. @Steven,是的,我有这种情况,这就是我发现这个问题的原因,但我需要避免重复收集。我在想如何解决这两个问题,我明白了。我写了另一个替代你的答案。 这个答案不正确 - 如果您按项目上的唯一标识符分组,那么您将只对单个项目求和。然后结果将是可枚举的匿名类型,其元素数与 Items 中的行数一样多。 @WhoIsNinja 是对的 - 您需要使用常量进行分组。【参考方案4】:

要对表格求和,请按常数分组:

from p in m.Items
group p by 1 into g
select new 
    SumTotal = g.Sum(x => x.Total),
    SumDone = g.Sum(x => x.Done)

【讨论】:

请问有人能发个vb.net版本吗? @Zeus 这是 VB.Net 代码,从 g In From p In m.ItemsGroup p By 1New With _ Key .SumTotal = g.Sum(Function(x) x.Total), _ Key .SumDone = g.Sum(Function(x) x.Done) _ 你可以在这里转换代码link【参考方案5】:

当您使用 group by 时,Linq 会创建一个新的项目集合,因此您有两个项目集合。

以下是解决这两个问题的方法:

    在一次迭代中对任意数量的成员求和并 避免重复您的项目集合

代码:

public static class LinqExtensions

  /// <summary>
  /// Computes the sum of the sequence of System.Double values that are obtained 
  /// by invoking one or more transform functions on each element of the input sequence.
  /// </summary>
  /// <param name="source">A sequence of values that are used to calculate a sum.</param>
  /// <param name="selectors">The transform functions to apply to each element.</param>    
  public static double[] SumMany<TSource>(this IEnumerable<TSource> source, params Func<TSource, double>[] selectors)
  
    if (selectors.Length == 0)
    
      return null;
    
    else
    
      double[] result = new double[selectors.Length];

      foreach (var item in source)
      
        for (int i = 0; i < selectors.Length; i++)
        
          result[i] += selectors[i](item);
        
      

      return result;
    
  

  /// <summary>
  /// Computes the sum of the sequence of System.Decimal values that are obtained 
  /// by invoking one or more transform functions on each element of the input sequence.
  /// </summary>
  /// <param name="source">A sequence of values that are used to calculate a sum.</param>
  /// <param name="selectors">The transform functions to apply to each element.</param>
  public static double?[] SumMany<TSource>(this IEnumerable<TSource> source, params Func<TSource, double?>[] selectors) 
   
    if (selectors.Length == 0)
    
      return null;
    
    else
    
      double?[] result = new double?[selectors.Length];

      for (int i = 0; i < selectors.Length; i++)
      
        result[i] = 0;
      

      foreach (var item in source)
      
        for (int i = 0; i < selectors.Length; i++)
        
          double? value = selectors[i](item);

          if (value != null)
          
            result[i] += value;
          
        
      

      return result;
    
  

这是你必须进行求和的方式:

double[] result = m.Items.SumMany(p => p.Total, q => q.Done);

这是一个通用示例:

struct MyStruct

  public double x;
  public double y;


MyStruct[] ms = new MyStruct[2];

ms[0] = new MyStruct()  x = 3, y = 5 ;
ms[1] = new MyStruct()  x = 4, y = 6 ;

// sum both x and y members in one iteration without duplicating the array "ms" by GROUPing it
double[] result = ms.SumMany(a => a.x, b => b.y);

如你所见

result[0] = 7 
result[1] = 11

【讨论】:

【参考方案6】:
//Calculate the total in list field values
//Use the header file: 

Using System.Linq;
int i = Total.Sum(G => G.First);

//By using LINQ to calculate the total in a list field,

var T = (from t in Total group t by Total into g select g.Sum(t => t.First)).ToList();

//Here Total is a List and First is the one of the integer field in list(Total)

【讨论】:

【参考方案7】:

弄清楚在我的其余代码中从何处提取总和或其他聚合使我感到困惑,直到我记得我构造的变量是一个 Iqueryable。假设我们的数据库中有一个由 Orders 组成的表,我们想为 ABC 公司生成一个摘要:

var myResult = from g in dbcontext.Ordertable
               group p by (p.CUSTNAME == "ABC") into q  // i.e., all of ABC company at once
               select new

    tempPrice = q.Sum( x => (x.PRICE ?? 0m) ),  // (?? makes sure we don't get back a nullable)
    tempQty = q.Sum( x => (x.QTY ?? 0m) )
;

现在有趣的部分—— tempPrice 和 tempQty 没有在任何地方声明,但它们必须是 myResult 的一部分,不是吗?按如下方式访问它们:

Console.Writeline(string.Format("You ordered 0 for a total price of 1:C",
                                 myResult.Single().tempQty,
                                 myResult.Single().tempPrice ));

也可以使用许多其他 Queryable 方法。

【讨论】:

【参考方案8】:

怎么样

   m.Items.Select(item => new  Total = item.Total, Done = item.Done )
          .Aggregate((t1, t2) => new  Total = t1.Total + t2.Total, Done = t1.Done + t2.Done );

【讨论】:

我认为这对 SQL 不友好。【参考方案9】:

使用辅助元组类,无论是您自己的还是(在 .NET 4 中)标准的,您都可以这样做:

var init = Tuple.Create(0, 0);

var res = m.Items.Aggregate(init, (t,v) => Tuple.Create(t.Item1 + v.Total, t.Item2 + v.Done));

res.Item1Total 列和Done 列的res.Item2 的总和。

【讨论】:

以上是关于在一个 LINQ 查询中获取两列的总和的主要内容,如果未能解决你的问题,请参考以下文章

复合求和:我想创建一个复合查询,它从两个不同的表中获取两列的单独总和,然后对它们求和

大家好,任何人都可以帮助我在PostgreSQL中按行获取两列的总和

从另一个子查询中获取列值

sql查询提供的jtable列总和

Linq查询以获取最多为N的所有数字(正数和负数),总和为数字K

LINQ 将列的总和添加到列表条目