Есть ли эффективный способ для промежуточных расчетов в Linq to Entities - PullRequest
4 голосов
/ 17 ноября 2011

У меня есть этот запрос, который использует 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)
});

Как вы можете видеть, эта часть повторяется несколько раз (100 м / х.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.

Ответы [ 3 ]

2 голосов
/ 17 ноября 2011

Предоставляет ли .ToTraceString() выполненный запрос запрос SQL, который вы можете профилировать? Легко заблудиться во всех этих вычислениях, но я уверен, что если вы захотите объединить все эти вычисления в одном запросе, производительность снизится. Другой способ уменьшить количество повторений в вычислениях - использовать ключевое слово let (для него нет метода расширения, поэтому вам нужно использовать «традиционный» LINQ). Это позволяет вам назначить переменную, которая может быть повторно использована в запросе. Но я сомневаюсь, что он будет работать лучше, чем ваш групповой подход.

from f in Fahrer
let meterFaktor = 100m / x.Sum(y =>.BasisMeter)
select new FahrerLigaEintrag()
{
    ...
}
1 голос
/ 17 ноября 2011

Думаю, я бы добавил это здесь, а не только в твиттер, прямо к Кристофу.Я думаю, что это много, чтобы попросить LINQ перевести.Да, это может быть сделано, но, как он может видеть, это не красиво, потому что LINQ to Entities должен использовать универсальные алгоритмы для обработки всего, что вы на это бросаете.Если бы у него была возможность добавить сохраненный процесс или представление в базу данных, я бы пошел по этому пути вместо этого.

И я также рекомендовал (лучшее, что я мог за 140 символов), чтобы он проверил EFProfiler (efprof).com) или новый профилировщик LLBLGen (llblgen.com) для профилирования запросов EF в Oracle.

0 голосов
/ 17 ноября 2011

Как насчет использования универсального делегата 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...