2011-11-17 66 views
4

我有這個查詢在幕後使用LinqToEntities。有沒有一種有效的方式來進行中間計算Linq to Entities

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

正如你可以看到這部分重複幾次(100米/ 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的情況下)。所以,我想知道是否有另一種方法來避免重複計算,或者如果這只是我能得到的最快速度。

打擾所有這些德語變量名稱。我相信你仍然有意義。

UPDATE

我能夠使用ToTraceString()的建議。有趣的是,在投影中,SQL包含18個(!!!)SELECT語句。沒有它,它只包含2個。

回答

1

想到我會在這裏添加這個,而不是直接在twitter上給克里斯托弗。我認爲LINQ需要翻譯很多。是的,它可以完成,但正如他所看到的,這並不是很好,因爲LINQ to Entities必須使用通用算法來處理所有拋出它的東西。如果他有可能將存儲過程或視圖添加到數據庫,那麼我會轉而使用該路線。

而且我還建議(我最好能在140個字符之內)檢查EFProfiler(efprof.com)或LLBLGen的新分析器(llblgen.com)來分析Oracle中的EF查詢。

2

.ToTraceString()對查詢執行的操作是否提供了可以配置的SQL查詢?在所有這些計算中很容易迷失,但我敢肯定,如果你想在單個查詢中進行所有這些計算,性能將會受到影響。另一種減少重複計算的方法是使用關鍵字let(它沒有擴展方法,所以你必須使用「傳統」的LINQ)。這個「讓」您可以分配一個可以在查詢中重新使用的變量。但我懷疑它會比你的分組方法更好。

from f in Fahrer 
let meterFaktor = 100m/x.Sum(y =>.BasisMeter) 
select new FahrerLigaEintrag() 
{ 
    ... 
} 
+0

感謝您的回答。 「讓」只是查詢語法糖,並轉換爲Select()投影。編譯器根本不知道查詢語法。這一切都歸結爲編譯器級別的擴展方法。不過,我可以按照你的建議使用ToTraceString()。我不知道,我在幾個月前試圖使用它,但沒有運氣,但現在它可能會工作,因爲底層devart oracle驅動程序的更新。 SQL看起來很糟糕,但我試圖進一步調查。很遺憾,我無法在此發佈。 – Christoph

+0

我更新了問題。事實證明,隨着投影的實施,SQL變得越來越複雜。不知道在一個查詢中是否有另一種解決方法。 – Christoph

+0

@Christoph我看不出去除投影會如何將SELECT的數量減少到2,因爲每個.Sum()都需要一個。把每個.Sum()調用放到投影中怎麼樣?我認爲你不會從中獲得太多的表現,但至少可以更清楚地瞭解所有涉及的屬性。我已經計算了10個.Sum(),儘管如此。 –

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) 
+0

如何將其轉換爲SQL。我很困惑。 – Christoph

+0

@Christoph:看起來像是因爲標題包含「Linq to Entities」而丟失了一些東西(對象?) – sll

+0

整個查詢是在IQueryable中建立起來的。 IQueryableProvider負責將表達式樹轉換爲適當的SQL查詢。因此,任何重構必須導致IQueryableProvider可以轉換成適當的SQL的查詢。 – Christoph

相關問題