简体   繁体   English

重构以下LINQ查询以使其性能更好,看起来更简单

[英]Refactoring the following LINQ query to perform better and look more simple

Consider a function (GetData) which returns the following result set where the first column is division id and the second one is "TotalSales": 考虑一个函数(GetData),该函数返回以下结果集,其中第一列是部门ID,第二列是“ TotalSales”:

DivisionID: 3     500
DivisionID: 3     500
DivisionID: 3     500

DivisionID: 4     800     
DivisionID: 4     800

DivisionID: 5     50

I need to write a LINQ query to get the following result: 我需要编写一个LINQ查询来获得以下结果:

DiviosnID 3: (500 * 3) - 500 = 1000
DiviosnID 4: (800 * 2) - 800 = 800
DiviosnID 5: 0 /*this since it's happening only once*/

So the total becomes: 1000 + 800 = 1800 因此总数变为:1000 + 800 = 1800

Finally, this value should be multiplied by -1, which results in -1800. 最后,该值应乘以-1,得出-1800。

The following LINQ query gets the job done, however, it's IMSHO scary. 下面的LINQ查询可以完成工作,但是,IMSHO令人恐惧。 The question is that whether it can be re-written to perform faster and look nicer?! 问题是,是否可以重写它以更快地执行并看起来更好? Please note there's a third column there, just like the TotalSales named "TotalPurchases" which I need to do the same calculation for. 请注意,这里有第三列,就像名为“ TotalPurchases”的TotalSales一样,我需要对其进行相同的计算。

GetData()
.Where(t => t.DivisionId != 0)
.GroupBy(t => t.DivisionId)
.Where(g => g.Count() > 1)
.Select(g => new MyEntity
{
    TotalSales = g.Sum(n => n.TotalSales) - (g.Sum(n => n.TotalSales) / g.Count()),
    TotalPurchases = g.Sum(n => n.TotalPurchases) - (g.Sum(n => n.TotalPurchases) / g.Count())
})
.Union(Enumerable.Repeat(new MyEntity(), 1))
.Aggregate((t1, t2) => new MyEntity
{
    TotalSales = -(t1.TotalSales + t2.TotalSales),
    TotalPurchases = -(t1.TotalPurchases + t2.TotalPurchases),
});

Thank you 谢谢

A quick first attempt: 快速的第一次尝试:

var consolidatedData = GetData()
        .GroupBy(t => t.DivisionId)
        .Where(g => g.Skip(1).Any(i => i.DivisionId != 0))
        .Select(g => new
        {
            TotalSales = -(g.Sum(n => n.TotalSales) - g.Average(n => n.TotalSales)),
            TotalPurchases = -(g.Sum(n => n.TotalPurchases) - g.Average(n => n.TotalPurchases))
        });

var overallSales = consolidatedData.Sum(i => i.TotalSales);
var overallPurchases = consolidatedData.Sum(i => i.TotalPurchases);

Using Skip, you avoid the potentially expensive Count() being run on every group - it just Skips one item, and sees if there's anything left. 使用“跳过”,可以避免在每个组上运行潜在昂贵的Count()-它仅跳过一项,然后查看是否还有剩余。

By building the result into an IEnumerable of anonyomous objects, the code is simplified - you can then query it afterewards when you want the final sums. 通过将结果构建到一个匿名对象的IEnumerable中,可以简化代码-然后,您可以在需要最终总和时查询它。 Note that Average is also used in place of Sum / Count. 请注意,平均值也用于代替总和/计数。

The Aggregate is removed - you only compute the final sum at the end when you need it. 聚合被删除-仅在需要时才计算最终总和。

My proposition: 我的主张:

var result = data
    .Where(t => t.DivisionID != 0)
    .GroupBy(t => t.DivisionID)
    .Select(g => new MyEntity
    {
        TotalSales = g.Sum(n => n.TotalSales) - g.Average(n => n.TotalSales),
        TotalPurchases = g.Sum(n => n.TotalPurchases) - g.Average(n => n.TotalPurchases)
    })
    .Aggregate(new MyEntity(), (t1, t2) => new MyEntity
    {
        TotalSales = t1.TotalSales - t2.TotalSales,
        TotalPurchases = t1.TotalPurchases - t2.TotalPurchases,
    });

Checking for Where(g => g.Count() > 1) is not necessary, because SUM and AVG for 1 element group is equal, so select will return 0. I also removed Union(Enumerable.Repeat(new MyEntity(), 1)) and I added seed to aggregate call - this is start value. Where(g => g.Count() > 1)检查Where(g => g.Count() > 1) ,因为1个元素组的SUM和AVG相等,因此select将返回0。我还删除了Union(Enumerable.Repeat(new MyEntity(), 1))然后将seed添加到聚合调用中-这是初始值。

Another one: 另一个:

var result = data
    .Where(t => t.DivisionID != 0)
    .GroupBy(t => t.DivisionID)
    .Select(g => new MyEntity
    {
        TotalSales = g.Sum(n => n.TotalSales) - g.Average(n => n.TotalSales),
        TotalPurchases = g.Sum(n => n.TotalPurchases) - g.Average(n => n.TotalPurchases)
    })
    .GroupBy(t => 0) // create single group
    .Select(g => new MyEntity
    {
        TotalSales = -g.Sum(t => t.TotalSales),
        TotalPurchases = -g.Sum(t => t.TotalPurchases)
    })
    .SingleOrDefault();

Assuming that TotalSales is constant for DivisionId you can use that: 假设TotalSales对于DivisionId是常量,则可以使用:

var result = data
    .Where(t => t.DivisionID != 0)
    .GroupBy(t => t.DivisionID)
    .Select(g => new MyEntity
    {
        TotalSales = g.Skip(1).Sum(n => n.TotalSales),
        TotalPurchases = g.Skip(1).Sum(n => n.TotalPurchases)
    })
    .Aggregate(new MyEntity(), (t1, t2) => new MyEntity
    {
        TotalSales = t1.TotalSales - t2.TotalSales,
        TotalPurchases = t1.TotalPurchases - t2.TotalPurchases,
    });

var result = data
    .Where(t => t.DivisionID != 0)
    .GroupBy(t => t.DivisionID)
    .Select(g => new MyEntity
    {
        TotalSales = g.Skip(1).Sum(n => n.TotalSales),
        TotalPurchases = g.Skip(1).Sum(n => n.TotalPurchases)
    })
    .GroupBy(t => 0) // create single group
    .Select(g => new MyEntity
    {
        TotalSales = -g.Sum(t => t.TotalSales),
        TotalPurchases = -g.Sum(t => t.TotalPurchases)
    })
    .SingleOrDefault();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM