重构以下LINQ查询以更好地执行并且看起来更简单

时间:2013-10-25 06:27:14

标签: c# linq

考虑一个函数(GetData),它返回以下结果集,其中第一列是分区id,第二列是“TotalSales”:

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

DivisionID: 4     800     
DivisionID: 4     800

DivisionID: 5     50

我需要编写一个LINQ查询来获得以下结果:

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

所以总数变为:1000 + 800 = 1800

最后,此值应乘以-1,结果为-1800。

以下LINQ查询完成了工作,然而,它是IMSHO可怕的。问题是它是否可以重写以更快地执行并且看起来更好?!请注意那里有第三列,就像名为“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),
});

谢谢

2 个答案:

答案 0 :(得分:0)

快速首次尝试:

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);

使用Skip,您可以避免在每个组上运行可能很昂贵的Count() - 只需跳过一个项目,然后查看是否还有其他内容。

通过将结果构建到IEnumerable的anonyomous对象中,代码被简化了 - 然后您可以在想要最终总和时查询它。请注意,平均值也用于代替Sum / Count。

聚合被删除 - 您只需在需要时计算最终总和。

答案 1 :(得分:0)

我的主张:

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,
    });

不需要检查Where(g => g.Count() > 1),因为1个元素组的SUM和AVG相等,所以select将返回0。 我还删除了Union(Enumerable.Repeat(new MyEntity(), 1)),我添加了seed来汇总调用 - 这是起始值。

另一个:

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();

假设TotalSales的{​​{1}}不变,您可以使用它:

DivisionId