3

Order などのオブジェクトのナビゲーション プロパティ (Items など) があります。アイテムを含む注文のリストを取得したい場合は、次のようにすることができます。

var orders = dbContext.Order.Include(o => i.Items);

これはうまく機能しますが、今は注文ごとに 3 つのアイテムのみを取得したいと考えており、これを達成するための最良の方法を考えています.

1 つの方法は、次の手順を実行することです。

var orders = 
    (from o in dbContext.Order
     join i in dbContext.Items on o.Id equals i.OrderId
     select new { o.Id, i })
    .GroupBy(o => o.Id)
    .SelectMany(i => i.Take(3))

生成された SQL は少し複雑ですが、これはうまく機能しますが、もっと直接的な (または効率的な) 方法があるかどうか疑問に思っています。

ありがとう、エリック

4

3 に答える 3

3

OUTER APPLYこれにより、 Itemsに対する上位3つのステートメントの形式で単純なSQLが生成されます。次に、linq-to-objectsを使用してグループ化を行う必要がありますが、必要なデータのみがサーバーから取得されています。

var orders = 
   (from o in dbContext.Order
    from i in (from x in dbContext.Items
               where o.Id == x.OrderId
               select x).Take(3).DefaultIfEmpty()
    select new
    {
        Order = o,
        Item = i
    }).AsEnumerable()
      .GroupBy(x => x.Order)
      .Select(x => new { Order = x.Key, Items = x.Select (y => y.Item ) });

また、注文エンティティなしで注文ごとに上位3つのアイテムのみが必要な場合。CROSS APPLYSQLのtopステートメントでアイテムを生成します。

var items = 
      from o in dbContext.Order
      from i in (from x in dbContext.Items
                 where o.Id == x.OrderId
                 select x).Take(3)
      select i;
于 2013-03-11T22:50:16.620 に答える
3

パフォーマンスはどれくらい悪いですか?許容範囲なら放っておけばいいのに。SQL でこれを行う簡単な方法もありません。通常、グループ化によって分割された を計算するサブクエリがROW_NUMBER作成され、行番号が より小さい行が返されますn

そのメカニズムを Linq に直接変換することはできないため、重大なパフォーマンスの問題でない限り、Linq を理解しやすくし、生成された SQL の複雑さについて心配する必要はありません。

また、すべてのアイテムを返し、Linq-to-Objects を使用してフィルタリングするパフォーマンスと比較することもできます。

もう 1 つのオプションは、これを Linq で行うのではなく、ストアド プロシージャとしてコーディングすることです。

于 2013-03-11T22:24:12.017 に答える
3
var orders = dbContext.Order
    .Select(o => new
    {
        Order = o,
        Items = o.Items.Take(3)
    })
    .AsEnumerable()
    .Select(a => a.Order)
    .ToList();

Order.Itemsこれにより、コレクションが上位 3 項目で自動的に満たされます。

  • 変更の追跡を無効にしない (上記のクエリには当てはまらない)
  • と の関係は多対多OrderItemはありません (注文とアイテムは通常 1 対多の関係にあるため、おそらくそうではありません)

編集

生成された SQL クエリは次のとおりです。

SELECT 
[Project2].[Id] AS [Id], 
[Project2].[C1] AS [C1], 
[Project2].[Id1] AS [Id1], 
[Project2].[OrderId] AS [OrderId], 
FROM (SELECT 
      [Extent1].[Id] AS [Id], 
      [Limit1].[Id] AS [Id1], 
      [Limit1].[OrderId] AS [OrderId], 
      CASE WHEN ([Limit1].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
      FROM  [dbo].[Orders] AS [Extent1]
      OUTER APPLY  (SELECT TOP (3) 
          [Extent2].[Id] AS [Id], 
          [Extent2].[OrderId] AS [OrderId], 
          FROM [dbo].[Items] AS [Extent2]
          WHERE [Extent1].[Id] = [Extent2].[OrderId] ) AS [Limit1]
) AS [Project2]
ORDER BY [Project2].[Id] ASC, [Project2].[C1] ASC
于 2013-03-11T23:38:57.183 に答える