在一个 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要对表格求和,请按常数分组:
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.Item1
是Total
列和Done
列的res.Item2
的总和。
【讨论】:
以上是关于在一个 LINQ 查询中获取两列的总和的主要内容,如果未能解决你的问题,请参考以下文章
复合求和:我想创建一个复合查询,它从两个不同的表中获取两列的单独总和,然后对它们求和
大家好,任何人都可以帮助我在PostgreSQL中按行获取两列的总和