查询到列表的转换速度非常慢

时间:2012-06-26 05:56:26

标签: c# linq

我从单个视图查询以检索指标列表。可以从该单个视图中检索指标的所有属性。

以下是代码:

data = new DataContext();

var core = from item in data.Items
           where countryIDs.Contains(item.CountryId)
           && indicatorIDs.Contains(item.IndicatorId)
           orderby item.Indicator
           select item;

var x = from item in core.Distinct()
        group item by new { item.IndicatorId, item.Indicator }
            into indicator
            select new
            {
                IndicatorID = indicator.Key.IndicatorId,
                IndicatorDescription = indicator.Key.Indicator,
                Genders = from g in core
                          where g.Gender != null
                          && g.IndicatorId == indicator.Key.IndicatorId
                          select new Gender
                          {
                              GenderID = g.GenderId,
                              GenderDescription = g.Gender
                          },
                HasGender = (from g in core
                             where g.Gender != null
                             && g.IndicatorId == indicator.Key.IndicatorId
                             select g.GenderId).Count() > 0,
                AreaTypes = from rat in core
                                     where rat.IndicatorId == indicator.Key.IndicatorId
                                     && rat.AreaType != null
                                     select new AreaType
                                     {
                                         AreaTypeId = rat.AreaTypeId,
                                         AreaDescription = rat.AreaType
                                     },
                HasAreaType = (from rat in core
                                        where rat.IndicatorId == indicator.Key.IndicatorId
                                        && rat.AreaType != null
                                        select rat.AreaTypeId).Count() > 0,
                Sectors = from s in core
                          where s.IndicatorId == indicator.Key.IndicatorId
                          && s.Sector != null
                          select new Sector
                          {
                              SectorID = s.SectorId,
                              Title = s.Sector
                          },
                HasSector = (from s in core
                             where s.IndicatorId == indicator.Key.IndicatorId
                             && s.Sector != null
                             select s.SectorId).Count() > 0
            };

List<Indicator> indicators = new List<Indicator>();
Indicator i = new Indicator();
foreach (var item in x)
{
    i = new Indicator()
    {
        IndicatorID = item.IndicatorID,
        IndicatorDescription = item.IndicatorDescription,
        Genders = item.Genders.ToList(),
        AreaTypes = item.AreaTypes.ToList(),
        Sectors = item.Sectors.ToList(),
        HasGender = item.HasGender,
        HasAreaType = item.HasAreaType,
        HasSector = item.HasSector
    };
    indicators.Add(i);
}
return indicators;

当它转换到x时,它会在到达foreach循环时变慢。 有没有办法让这个查询更快地转换为列表?谢谢。

3 个答案:

答案 0 :(得分:4)

看起来你正在进行大量不必要的嵌套查询。

您的core查询在返回项目之前正在进行一些相对昂贵的过滤和排序。最好只执行一次此查询。

但是,您在此查询中执行了六次不必要的连接。

例如,您的查询Genders正在重新查询core,只保留您已经分组的IndicatorId项目!如果我可以假设item.Indicatoritem.IndicatorId一对一,则您的论坛indicator已经包含此子集。

您正在查询AreaTypes&amp; Sectors以同样的方式。

现在HasGenderHasAreaType和&amp; HasSector每个重复以上查询并强制.Count()每个查询,以检查该值是否大于零。这是一种浪费,因为.Any()将为您更便宜地检查至少一个值。

现在要测试访问core查询的次数,我创建了这个测试代码:

var countryIDs = Enumerable.Range(0, 100).ToArray();
var indicatorIDs = Enumerable.Range(0, 100).ToArray();

data.Items.AddRange(
    Enumerable
        .Range(0, 100)
        .Select(n =>
            new Item()
            {
                CountryId = n,
                IndicatorId = n,
                Indicator = "Indicator",
                GenderId = n,
                Gender = "Gender",
                AreaTypeId = n,
                AreaType = "Area",
                SectorId = n,
                Sector = "Sector",
            }));

我将core修改为如下所示:

var counter = 0;
var core =
    (from item in data.Items
    where countryIDs.Contains(item.CountryId)
        && indicatorIDs.Contains(item.IndicatorId)
    orderby item.Indicator
    select item).Do(_ => counter++);

Do运算符来自Reactive Extensions System.Interactive程序集。

运行代码我得到以下结果:

counter == 60100

由于我在集合中放了100个项目,这告诉我你的查询正在调用core新执行601次!

这可以很容易地改为执行core

首先我将core修改为:

var query =
    from item in data.Items
    where countryIDs.Contains(item.CountryId)
        && indicatorIDs.Contains(item.IndicatorId)
    orderby item.Indicator
    select item;

var core = query.ToArray();

.ToArray()将查询结果带入内存。

然后将x查询修改为如下所示:

var x =
    from item in core.Distinct()
    group item by new
    {
        item.IndicatorId,
        item.Indicator
    } into indicator
    let Genders = (
        from g in indicator
        where g.Gender != null
        select new Gender
        {
            GenderID = g.GenderId,
            GenderDescription = g.Gender,
        }).ToList()
    let AreaTypes = (
        from rat in indicator
        where rat.AreaType != null
        select new AreaType
        {
            AreaTypeId = rat.AreaTypeId,
            AreaDescription = rat.AreaType,
        }).ToList()
    let Sectors = (
        from s in indicator
        where s.Sector != null
        select new Sector
        {
            SectorID = s.SectorId,
            Title = s.Sector,
        }).ToList()
    select new Indicator()
    {
        IndicatorID = indicator.Key.IndicatorId,
        IndicatorDescription = indicator.Key.Indicator,
        Genders = Genders,
        AreaTypes = AreaTypes,
        Sectors = Sectors,
        HasGender = Genders.Any(),
        HasAreaType = AreaTypes.Any(),
        HasSector = Sectors.Any(),
    };

请注意,我正计算每个GendersAreaTypes和&amp;仅Sectors一次,并将每个创建为列表。这允许我更改x以立即生成Indicator的实例。

现在indicators列表的最终创建很简单:

var indicators = x.ToList();

当我在这个方法上使用我的样本数据时,结果如下:

counter == 100

这意味着此查询只会触及原始core一次查询!

然后我检查了当我将原始样本数据增加到1,000个项目时嵌套的行为 - 我使用新代码获得了一次点击,使用原始代码获得了6,001次点击 - 并且它变得更快,更慢。

请记住,LINQ是懒惰计算的,因此在您定义查询的地方不会执行执行,而是执行它的位置。

所以这里的建议是,在内存允许的情况下,您应该尽快执行查询,将数据存入内存,然后执行一次计算。

答案 1 :(得分:0)

对于初学者,更改每个Count()&gt; 0进入Any()方法,Count将强制完全扫描您正在查询的表。

如果这不能为您提供所需的性能提升,请尝试重写您的查询。我认为,如果您首先将数据投影到匿名类型,那么性能会更高,而然后按匿名类型分组。

答案 2 :(得分:0)

您的查询中有一些where子句(例如,s.IndicatorId == indicator.Key.IndicatorId)

尝试在此处使用加入语法,这将使其更快。 即您的案例中的核心连接指标。

之类的东西

您的

版本
from g in core 
where g.Gender != null && g.IndicatorId == indicator.Key.IndicatorId

会得到类似的东西

From g In core Join indi In indicator 
on g.IndicatorId Equals indi.Key.IndicatorId

Why is LINQ JOIN so much faster than linking with WHERE?