更换include()调用选择()

问题描述 投票:2回答:2

我试着去消除使用的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);

我一直有与数据库的问题,其中运行时间远远太长,这主要原因是包括()调用拉一切。这是观看该是从这个示出大量的被返回不必要的信息而产生的合成SQL查询返回的表明显。一,你学我猜的东西。该数据库收集了大量其中有很多记录值的数据点。每个记录的值被映射到一个记录类,其可以具有一个记录别名。

我曾尝试创建一个选择()作为替代,但我只是无法弄清楚如何构建正确的选择,并保持实体层次正确加载。即相关实体被加载到数据库不必要的调用。

有没有人有备用的解决方案,可以迅速启动我解决这个问题。

如果需要添加病更详细。

c# entity-framework iqueryable
2个回答
2
投票

你是对的。一个数据库查询的速度较慢的部分是从DBMS所选择的数据到本地进程运输。因此,明智的做法是限制这一点。

每个TimeDomainDataPoint有一个主键。这RecordValues的所有TimeDomainDataPoint有等于该主键的值的外键TimeDomainDataPointId

所以,如果TimeDomainDataPoint与ID 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 recordValues数据点。如果你是无论如何选择RecordAlias,可考虑做后你选择这个地点:

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

我真的不知道这是否是快。如果你的数据库管理系统内部先建立与所有连接表中的所有列一个完整的表,然后扔掉,未选中的列,那么就不会有所作为。然而,如果只创建与实际使用的列的表,则内部表会更小。这可能会更快。


0
投票

你的问题是层次结构,您query.In为了减少这个问题创建关系表中获取结果的其它查询,如下所示连接:

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

以及其他请求分贝:

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

这种方法您的查询没有内部连接。

© www.soinside.com 2019 - 2024. All rights reserved.