我有这个在幕后使用LinqToEntities的查询。
(...)
.GroupBy(x => x.FahrerID)
.Select(x => new FahrerligaEintrag()
{
FahrerID = x.Key,
FahrerFullName = string.Empty,
VollgasKmAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * (x.Sum(y => y.Gas100ProzentInMeter.Value) + x.Sum(y => y.Gas90ProzentInMeter.Value)),
LeerlaufInProzent = (100m / x.Sum(y => y.BasisSekunden)) * x.Sum(y => y.LeerlaufInSekunden.Value),
VerbrauchLiterAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * x.Sum(y => y.VerbrauchInLiter.Value) * 1000,
RollenKmAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * x.Sum(y => y.RollenInMeter.Value),
TempomatKmAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * x.Sum(y => y.TempomatInMeter.Value),
GeschwindigkeitsuebertretungenAnzahlAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * 1000 * x.Sum(y => y.UebertretungenAnzahl.Value),
GangwechselAnzahlAufHundertKm = (100m / x.Sum(y => y.BasisMeter)) * 1000 * x.Sum(y => y.GangwechselAnzahl.Value)
});
如您所见,此部分重复多次(100m / x.Sum(y => y.BasisMeter))。
在Linq to Objects中,首先投射到匿名类来计算因子以避免重复计算是很自然的。像这样:
.GroupBy(x => x.FahrerID)
.Select(x => new
{
Grouping = x,
BasisMeterFaktor = 100m / x.Sum(y => y.BasisMeter),
BasisSekundenFaktor = 100m /x.Sum(y => y.BasisSekunden)
})
.Select(x => new FahrerligaEintrag()
{
FahrerID = x.Grouping.Key,
FahrerFullName = string.Empty,
VollgasKmAufHundertKm = x.BasisMeterFaktor * (x.Grouping.Sum(y => y.Gas100ProzentInMeter.Value) + x.Grouping.Sum(y => y.Gas90ProzentInMeter.Value)),
LeerlaufInProzent = x.BasisSekundenFaktor * x.Grouping.Sum(y => y.LeerlaufInSekunden.Value),
VerbrauchLiterAufHundertKm = x.BasisMeterFaktor * x.Grouping.Sum(y => y.VerbrauchInLiter.Value) * 1000,
RollenKmAufHundertKm = x.BasisMeterFaktor * x.Grouping.Sum(y => y.RollenInMeter.Value),
TempomatKmAufHundertKm = x.BasisMeterFaktor * x.Grouping.Sum(y => y.TempomatInMeter.Value),
GeschwindigkeitsuebertretungenAnzahlAufHundertKm = x.BasisMeterFaktor * 1000 * x.Grouping.Sum(y => y.UebertretungenAnzahl.Value),
GangwechselAnzahlAufHundertKm = x.BasisMeterFaktor * 1000 * x.Grouping.Sum(y => y.GangwechselAnzahl.Value)
});
但是,在LinqToEntities中,这会导致SQL代码性能下降。至少我使用的这个Oracle后端(我无法通过配置文件向我显示SQL)。所以,我想知道是否有另一种方法可以避免重复计算,或者这是否是我能获得的最快速度。
请原谅所有德国变量名称。我相信你仍然有意义。
更新
我能够按照建议使用ToTraceString()。有趣的是,对于投影,SQL包含18(!!!)SELECT语句。没有它,它只包含2.
答案 0 :(得分:2)
查询上执行的.ToTraceString()
是否提供了可以分析的SQL查询?在所有这些计算中很容易迷失,但我敢肯定,如果你想在单个查询中进行所有这些计算,性能将会受到影响。减少重复计算的另一种方法是使用let
关键字(没有扩展方法,所以你必须使用“传统”LINQ)。这个“允许”您指定一个可以在查询中重用的变量。但我怀疑它会比你的分组方法更好。
from f in Fahrer
let meterFaktor = 100m / x.Sum(y =>.BasisMeter)
select new FahrerLigaEintrag()
{
...
}
答案 1 :(得分:1)
以为我会在这里添加这个,而不仅仅是在Twitter上直接给Christoph。我认为LINQ要翻译的内容很多。是的,它可以完成,但正如他所看到的,它并不漂亮,因为LINQ to Entities必须使用通用算法来处理你抛出的任何东西。如果他有可能将存储过程或视图添加到数据库中,我会转而使用该路径。
我还推荐了(140个字符中最好的),他查看了EFProfiler(efprof.com)或LLBLGen的新分析器(llblgen.com),以便在Oracle中分析EF查询。
答案 2 :(得分:0)
使用通用委托Func<Tx, Ty, TResult>
怎么样?
// declare out of query and provide valid types for x, y, result
Func<TX, TY, TResult> basisMeterFaktorAlgo;
// set formula once
basisMeterFaktorAlgo = (x, y) => 100m / x.Sum(y => y.BasisMeter);
// call it in query
basicMeterFactor = basisMeterFaktorAlgo(xValue, yValue)