1

インジケーターのリストを取得するために、単一のビューからクエリを実行しています。インジケーターのすべてのプロパティは、その単一のビューから取得できます。

コードは次のとおりです。

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ループに到達すると速度が低下します。このクエリをリストにすばやく変換する方法はありますか?ありがとうございました。

4

3 に答える 3

4

不要なネストされたクエリをたくさん実行しているようです。

クエリcoreは、アイテムを返す前に、比較的コストのかかるフィルタリングと並べ替えを実行しています。このクエリは1回だけ実行することをお勧めします。

ただし、このクエリでは6つの不要な結合を実行しています。

Gendersたとえば、クエリは、すでにグループ化したものcoreと同じアイテムのみを保持して再クエリします。それが1対1であるIndicatorIdと仮定できる場合、あなたのグループにはすでにこのサブセットが含まれています。item.Indicatoritem.IndicatorIdindicator

同じ方法でAreaTypes&をクエリしています。Sectors

ここでHasGender、、、HasAreaTypeおよびHasSectorそれぞれが上記のクエリを繰り返し.Count()、値がゼロより大きいかどうかを確認するためだけに、それぞれにaを強制します。.Any()少なくとも1つの値をはるかに安価にチェックするため、これは無駄です。

ここで、クエリにアクセスする回数をテストするために、次の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オペレーターは、R​​eactiveExtensionsSystem.Interactiveアセンブリの出身です。

コードを実行すると、次の結果が得られました。

counter == 60100

コレクションに100個のアイテムを入れたので、これは、クエリがcore601回の新しい実行を呼び出していることを示しています。

これは、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(),
    };

Genders、、AreaTypes&のそれぞれをSectors1回だけ計算し、それぞれをリストとして作成していることに注意してください。xこれにより、のインスタンスをすぐに生成するように変更できましたIndicator

これで、リストの最終的な作成indicatorsは簡単になりました。

var indicators = x.ToList();

このメソッドでサンプルデータを使用したときの結果は次のとおりです。

counter == 100

これは、このクエリが元のcoreクエリに1回だけヒットすることを意味します。

次に、元のサンプルデータを1,000アイテムに増やしたときに、ネストがどのように動作するかを確認しました。新しいコードで1回ヒットし、元のコードで6,001ヒットしましたが、非常に遅くなりました。

LINQは遅延計算されるため、クエリを定義する場所ではなく、クエリを実行する場所で実行されることに注意してください。

したがって、ここでのアドバイスは、メモリが許せば、データをメモリに取り込むために実行可能な限り早くクエリを実行してから、計算を1回だけ実行する必要があるということです。

于 2012-06-26T07:43:01.350 に答える
0

手始めに、すべてのCount()> 0をAny()メソッドに変更します。カウントは、クエリしているテーブルのフルスキャンを強制します。

これで目的のパフォーマンスが得られない場合は、クエリを書き直してみてください。最初にデータを匿名タイプに投影し、次にその匿名タイプでグループ化すると、パフォーマンスが向上すると思います。

于 2012-06-26T06:07:04.203 に答える
0

クエリにwhere句がいくつかあります(たとえば、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

LINQ JOINがWHEREとのリンクよりもはるかに高速なのはなぜですか?

于 2012-06-26T06:09:49.700 に答える