Замена вызовов Include () на Select () - PullRequest
0 голосов
/ 04 февраля 2019

Я пытаюсь исключить использование вызовов Include () в этом определении IQueryable:

return ctx.timeDomainDataPoints.AsNoTracking()
   .Include(dp => dp.timeData)
   .Include(dp => dp.RecordValues.Select(rv => rv.RecordKind).Select(rk => rk.RecordAlias).Select(fma => fma.RecordAliasGroup))
   .Include(dp => dp.RecordValues.Select(rv => rv.RecordKind).Select(rk => rk.RecordAlias).Select(fma => fma.RecordAliasUnit))
   .Where(dp => dp.RecordValues.Any(rv => rv.RecordKind.RecordAlias != null))
   .Where(dp => dp.Source == 235235)
   .Where(dp => dp.timeData.time >= start && cd.timeData.time <= end)
   .OrderByDescending(cd => cd.timeData.time);

У меня возникли проблемы с базой данных, где время выполнения слишком длинное и основная причинаэто вызовы Include () тянут все.Это очевидно при просмотре таблицы, которая возвращается из результирующего запроса SQL, сгенерированного из этого, показывающего много ненужной возвращаемой информации.Я думаю, одна из вещей, которую ты изучаешь.База данных имеет большой набор точек данных, которые имеют много записанных значений.Каждое записанное значение сопоставляется с видом записи, который может иметь псевдоним записи.

Я пытался создать Select () в качестве альтернативы, но я просто не могу понять, как создать правильный Select, а также сохранить сущностьиерархия правильно загружена.Т.е. связанные сущности загружаются ненужными вызовами в БД.

Есть ли у кого-нибудь альтернативные решения, которые могут помочь мне решить эту проблему.

Я добавлю больше подробностей, если это необходимо.

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

Вы правы.Одна из более медленных частей запроса к базе данных - это передача выбранных данных из СУБД в локальный процесс.Следовательно, разумно ограничить это.

Каждый TimeDomainDataPoint имеет первичный ключ.Все RecordValues этого TimeDomainDataPoint имеют внешний ключ TimeDomainDataPointId со значением, равным этому первичному ключу.

Так что если TimeDomainDataPoint с идентификатором 4 имеет тысячу RecordValues, то у каждого RecordValue будет внешний ключсо значением 4. Было бы бесполезно передавать это значение 4 1001 раз, в то время как вам нужно только один раз.

При запросе данных всегда используйте Выберите и выберитетолько те свойства, которые вы действительно планируете использовать.Используйте Включить только если вы планируете обновить извлеченные включенные элементы.

Следующее будет намного быстрее:

var result = dbContext.timeDomainDataPoints
    // first limit the datapoints you want to select
    .Where(datapoint => d.RecordValues.Any(rv => rv.RecordKind.RecordAlias != null))
    .Where(datapoint => datapoint.Source == 235235)
    .Where(datapoint => datapoint.timeData.time >= start
                     && datapoint.timeData.time <= end)
    .OrderByDescending(datapoint => datapoint.timeData.time)

    // then select only the properties you actually plan to use
    Select(dataPoint => new
    {
        Id = dataPoint.Id,
        RecordValues = dataPoint.RecordValues
            .Where(recordValues => ...)           // if you don't want all RecordValues
            .Select(recordValue => new
            {
                // again: select only the properties you actually plan to use:
                Id = recordValue.Id,
                // not needed, you know the value: DataPointId = recordValue.DataPointId,
                RecordKinds = recordValues.RecordKinds
                    .Where(recordKind => ...) // if you don't want all recordKinds
                    .Select(recordKind => new
                    {
                         ... // only the properties you really need!
                    })
                    .ToList(),
                 ...
            })
            .ToList(),

        TimeData = dataPoint.TimeData.Select(...),
        ...
    });

Возможное улучшение

Деталь:

.Where(datapoint => d.RecordValues.Any(rv => rv.RecordKind.RecordAlias != null))

используется для выборки только точек данных, имеющих значения записи с ненулевым RecordAlias.Если вы все равно выбираете RecordAlias, подумайте над тем, чтобы сделать это, где после вашего выбора:

.Select(...)
.Where(dataPoint => dataPoint
       .Where(dataPoint.RecordValues.RecordKind.RecordAlias != null)
       .Any());

Я не совсем уверен, что это быстрее.Если ваша система управления базами данных внутренне сначала создает полную таблицу со всеми столбцами всех объединенных таблиц, а затем отбрасывает столбцы, которые не выбраны, это не будет иметь значения.Однако, если он создает таблицу только с теми столбцами, которые фактически использует, то внутренняя таблица будет меньше.Это может быть быстрее.

0 голосов
/ 04 февраля 2019

ваша проблема - иерархические соединения в вашем запросе. Чтобы уменьшить эту проблему, создайте другой запрос для получения результата из таблицы отношений следующим образом:

var items= ctx.timeDomainDataPoints.AsNoTracking().Include(dp =>dp.timeData).Include(dp => dp.RecordValues);
var ids=items.selectMany(item=>item.RecordValues).Select(i=>i.Id);

и по другому запросу в db:

  var otherItems= ctx.RecordAlias.AsNoTracking().select(dp =>dp.RecordAlias).where(s=>ids.Contains(s.RecordKindId)).selectMany(s=>s.RecordAliasGroup)

для этого подхода ваш запрос не имеет внутренних объединений.

...