2016-11-13 6 views
0

私は、このエンティティを持っている:このLinqクエリ(ピボットの一種)を改善するにはどうすればよいですか?

public class Delivery 
    { 
     public int Id { get; set; } 
     public int ProductId { get; set; } 
     public int CustomerId { get; set; } 
     public int Quantity { get; set; } 
     public DateTime DeliveryDate { get; set; } 
     public virtual Product Product { get; set; } 
     public virtual Customer Customer { get; set; } 
    } 

私は一週間で配達を表示したいので、私はこのクエリを記述します。

public override IEnumerable GetModelData(ApplicationDbContext context) 
      { 
       return context.Deliveries.GroupBy(x => x.Product).Select(x => new 
       { 
        Id=x.Key.Id, 
        Product = x.Key.Name, 
        Wk1 =(int?) x.Where(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 1).Sum(a => a.Quantity), 
... 
... 
... 
        Wk46 = (int?)x.Where(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 46).Sum(a => a.Quantity), 
        Wk47 = (int?)x.Where(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 47).Sum(a => a.Quantity), 
        Wk48 = (int?)x.Where(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 48).Sum(a => a.Quantity), 
        Wk49 = (int?)x.Where(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 49).Sum(a => a.Quantity), 
        Wk50 = (int?)x.Where(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 50).Sum(a => a.Quantity), 
        Wk51 = (int?)x.Where(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 51).Sum(a => a.Quantity), 
        Wk52 = (int?)x.Where(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 52).Sum(a => a.Quantity), 
       }).ToList(); 
      } 

が小さいクエリと予想オブジェクトを取得することが可能ですか?

私は配信データベーステーブルで約100サンプルの行を持っており、この方法でデータを取得するのが最良の方法ではないと思います。

クエリは機能していますが、この種のクエリを書くためのより良い方法があるかどうかを知りたいだけです。

+0

ロジックやコードがわかりません。週ごとに配送を表示したいとします。あなたはもっと詳しく説明できますか? 'Wk1、Wk2、etc'とは何ですか? –

+0

私は53列のテーブルを表示しています。最初の列はProductNameで、次の52列はweek1(wk1)からweek52(wk52)までの週です。上のクエリでは、小さなクエリを表示するためにいくつかの行を削除しました。 –

+0

うわー、あなたは生成されたSQLをチェックしましたか?私はそれがグループ化の結果に関する52の 'Where'ステートメントのためにそれがモンスターであると期待します。どのORMでbtwを使用していますか? –

答えて

1

LINQクエリを短くする唯一の方法は、プログラマチックにセレクタを生成することです。

しかし、生成されたSQLクエリをより短く(より効率的に)する方法は間違いありません。代わりに、うまく変換されないWhere(condition).Sum(expr)コンストラクトの、より良いSQLを生成し、条件付き合計、すなわちSum(condition ? expr : null)使用:私はSQLクエリでグループ化を行うが、その後メモリに旋回を行うだろう

Wk1 = x.Sum(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 1 ? a.Quantity : (int?)null), 
Wk2 = x.Sum(a => SqlFunctions.DatePart("wk", a.DeliveryDate) == 2 ? a.Quantity : (int?)null), 
... 
1

を。

public static IEnumerable GetModelData(ApplicationDbContext context) 
{ 
    return context.Deliveries 
     .GroupBy(x => new { x.Product.Id, x.Product.Name, Week = SqlFunctions.DatePart("wk", x.DeliveryDate) }) 
     .Select(x => new 
     { 
      Id = x.Key.Id, 
      Product = x.Key.Name, 
      Week = x.Key.Week, 
      Quantity = x.Sum(a => a.Quantity), 
     }) 
     .AsEnumerable() 
     .GroupBy(x => new { x.Id, x.Product }) 
     .Select(x => new 
     { 
      Id = x.Key.Id, 
      Product = x.Key.Product, 
      Wk1 = x.Sum(a => a.Week == 1 ? a.Quantity : 0), 
      Wk2 = x.Sum(a => a.Week == 2 ? a.Quantity : 0), 
      Wk51 = x.Sum(a => a.Week == 52 ? a.Quantity : 0), 
      Wk52 = x.Sum(a => a.Week == 53 ? a.Quantity : 0), 
     }) 
     .ToList(); 
} 

.AsEnumerable()の上にあるものはすべて、データベースに対して1つのSQL文として実行され、その下のすべてがメモリ内で実行されます。

実行されるSQLのトレースです。

SELECT 
    [GroupBy1].[K1] AS [ProductId], 
    [GroupBy1].[K2] AS [Name], 
    [GroupBy1].[K3] AS [C1], 
    [GroupBy1].[A1] AS [C2] 
    FROM (SELECT 
     [Join1].[K1] AS [K1], 
     [Join1].[K2] AS [K2], 
     [Join1].[K3] AS [K3], 
     SUM([Join1].[A1]) AS [A1] 
     FROM (SELECT 
      [Extent1].[ProductId] AS [K1], 
      [Extent2].[Name] AS [K2], 
      DATEPART(wk, [Extent1].[DeliveryDate]) AS [K3], 
      [Extent1].[Quantity] AS [A1] 
      FROM [dbo].[Deliveries] AS [Extent1] 
      INNER JOIN [dbo].[Products] AS [Extent2] ON [Extent1].[ProductId] = [Extent2].[Id] 
     ) AS [Join1] 
     GROUP BY [K1], [K2], [K3] 
    ) AS [GroupBy1] 

.AsEnumerable()を削除すると、すべてがサーバー上で実行されます。ここにSQLトレースがあります。

SELECT 
    [GroupBy2].[K1] AS [ProductId], 
    [GroupBy2].[K2] AS [Name], 
    [GroupBy2].[A1] AS [C1], 
    [GroupBy2].[A2] AS [C2], 
    [GroupBy2].[A3] AS [C3], 
    [GroupBy2].[A4] AS [C4] 
    FROM (SELECT 
     [GroupBy1].[K1] AS [K1], 
     [GroupBy1].[K2] AS [K2], 
     SUM([GroupBy1].[A1]) AS [A1], 
     SUM([GroupBy1].[A2]) AS [A2], 
     SUM([GroupBy1].[A3]) AS [A3], 
     SUM([GroupBy1].[A4]) AS [A4] 
     FROM (SELECT 
      [GroupBy1].[K1] AS [K1], 
      [GroupBy1].[K2] AS [K2], 
      CASE WHEN (1 = [GroupBy1].[K3]) THEN [GroupBy1].[A1] ELSE 0 END AS [A1], 
      CASE WHEN (2 = [GroupBy1].[K3]) THEN [GroupBy1].[A1] ELSE 0 END AS [A2], 
      CASE WHEN (52 = [GroupBy1].[K3]) THEN [GroupBy1].[A1] ELSE 0 END AS [A3], 
      CASE WHEN (53 = [GroupBy1].[K3]) THEN [GroupBy1].[A1] ELSE 0 END AS [A4] 
      FROM (SELECT 
       [Join1].[K1] AS [K1], 
       [Join1].[K2] AS [K2], 
       [Join1].[K3] AS [K3], 
       SUM([Join1].[A1]) AS [A1] 
       FROM (SELECT 
        [Extent1].[ProductId] AS [K1], 
        [Extent2].[Name] AS [K2], 
        DATEPART(wk, [Extent1].[DeliveryDate]) AS [K3], 
        [Extent1].[Quantity] AS [A1] 
        FROM [dbo].[Deliveries] AS [Extent1] 
        INNER JOIN [dbo].[Products] AS [Extent2] ON [Extent1].[ProductId] = [Extent2].[Id] 
       ) AS [Join1] 
       GROUP BY [K1], [K2], [K3] 
      ) AS [GroupBy1] 
     ) AS [GroupBy1] 
     GROUP BY [K1], [K2] 
    ) AS [GroupBy2] 
関連する問題