1

1 つの linq 文だけで何かを解決しようとしていますが、これが可能かどうかはわかりません。次のフィールドを持つ PRICES という名前のテーブルが 1 つあります。

 pkey: int
 region: int?
 product_type: int
 product_size: int
 price: double
 desc: string

一意のキーは次のとおりです: product_type + product_size

すべての行を返すクエリを実行したい WHERE region == 17 (これは私の最初の行セットです) AND region が null であるすべての行を追加したい (これは私の 2 番目の行セットです) しかし、両方のセットで同じ product_type と product_size が必要です。最終結果には最初のセットの行だけが必要です。

例:

pkey | region | product_type | product_size | price | desc

 1,    null,    20,            7,             2.70,   salad1    
 2,    null,    20,            3,             2.50,   salad7    
 3,    17,      20,            7,             1.90,   saladspecial    
 4,    17,      20,            5,             2.20,   other

これを返すlinqクエリが必要です:

 2,    null,    20,            3,             2.50,   salad7    
 3,    17,      20,            7,             1.90,   saladspecial    
 4,    17,      20,            5,             2.20,   other

(pkey 3 の行は product_type と product_size が同じであるため、pkey 1 の行は破棄されることに注意してください)

var query1 = from p in PRICES where p.region == 17    
             select p;

var query2 = from p in PRICES where p.region is null     
             select p;

質問:

  1. query1 と query2 を結合して期待される出力を取得する方法は?

  2. 1つのクエリだけで実行できますか?

4

2 に答える 2

2

17次のクエリは、 regionまたはの価格のみを選択nullし、一意のキーでグループ化します{ p.product_type, p.product_size }。次に、 group に region の価格が少なくとも 1 つ含まれているかどうかを確認します17。はいの場合、グループからこの地域のすべての価格を選択します (地域の価格はスキップしnullます)。それ以外の場合は、グループ全体を返します (null 領域のみがあります)。

var query = from p in PRICES.Where(x => x.region == 17 || x.region == null)
            group p by new { p.product_type, p.product_size } into g
            from pp in g.Any(x => x.region == 17) ? 
                       g.Where(x => x.region == 17) : g
            select pp;

入力:

1 null 20 7 2.7 salad1       // goes to group {20,7} with region 17 price
2 null 20 3 2.5 salad7       // goes to group {20,3} without region 17 prices
3   17 20 7 1.9 saladspecial // goes to group {20,7}
4   17 20 5 2.2 other        // goes to group {20,5}

出力:

2 null 20 3 2.5 salad7 
3   17 20 7 1.9 saladspecial
4   17 20 5 2.2 other

上記の編集クエリは、メモリ内のオブジェクト (つまり、LINQ to Objects) で正常に動作しますが、LINQ to Entitis はそれほど強力ではありません。ネストされたクエリはサポートされていません。したがって、Entity Framework の場合、2 つのクエリが必要になりnullます。1717

var pricesWithoutRegion = 
            db.PRICES.Where(p => p.region == 17 || p.region == null)
              .GroupBy(p => new { p.product_type, p.product_size })
              .Where(g => !g.Any(p => p.region == 17))
              .SelectMany(g => g);

var query = db.PRICES.Where(p => p.region == 17).Concat(pricesWithoutRegion);

実際、EF はサーバーへの 1 つのクエリで両方のサブクエリをUNION実行します。次の SQL が生成されます (画面に合わせてdess列とprice列を削除しました)。

SELECT [UnionAll1].[pkey] AS [C1], 
       [UnionAll1].[region] AS [C2], 
       [UnionAll1].[product_type] AS [C3], 
       [UnionAll1].[product_size] AS [C4]
FROM (SELECT [Extent1].[pkey] AS [pkey], 
             [Extent1].[region] AS [region], 
             [Extent1].[product_type] AS [product_type], 
             [Extent1].[product_size] AS [product_size]
      FROM [dbo].[Prices] AS [Extent1] WHERE 17 = [Extent1].[region]
UNION ALL
   SELECT [Extent4].[pkey] AS [pkey], 
          [Extent4].[region] AS [region], 
          [Extent4].[product_type] AS [product_type], 
          [Extent4].[product_size] AS [product_size]
   FROM (SELECT DISTINCT [Extent2].[product_type] AS [product_type], 
                         [Extent2].[product_size] AS [product_size]
         FROM [dbo].[Prices] AS [Extent2]
         WHERE ([Extent2].[region] = 17 OR [Extent2].[region] IS NULL) AND 
               (NOT EXISTS 
                (SELECT 1 AS [C1] FROM [dbo].[Prices] AS [Extent3]
                 WHERE ([Extent3].[region] = 17 OR [Extent3].[region] IS NULL)
                       AND ([Extent2].[product_type] = [Extent3].[product_type])
                       AND ([Extent2].[product_size] = [Extent3].[product_size])
                       AND (17 = [Extent3].[region])
                 ))) AS [Distinct1]
   INNER JOIN [dbo].[Prices] AS [Extent4] 
       ON ([Extent4].[region] = 17 OR [Extent4].[region] IS NULL)
          AND ([Distinct1].[product_type] = [Extent4].[product_type])
          AND ([Distinct1].[product_size] = [Extent4].[product_size]))
   AS [UnionAll1]

GroupByところで、条件付きの内部結合に変換されたことは私にとって驚きです。

于 2013-08-01T21:42:40.007 に答える
1

1 つのクエリを使用する必要があると思いますが、2 つのクエリを使用する場合は、何かを繰り返す必要があります。

//for 2 queries
var query = query1.Union(query2.Except(query2.Where(x=>query1.Any(y=>x.product_type==y.product_type&&x.product_size==y.product_size))))
                  .OrderBy(x=>x.pkey);

//for 1 query
//the class/type to make the group key
public class GroupKey
{
        public int ProductType { get; set; }
        public int ProductSize { get; set; }
        public override bool Equals(object obj)
        {
            GroupKey gk = obj as GroupKey;
            return ProductType == gk.ProductType && ProductSize == gk.ProductSize;
        }
        public override int GetHashCode()
        {
            return ProductSize ^ ProductType;
        }
}
//-------
var query = list.Where(x => x.region == 17 || x.region == null)
                .GroupBy(x => new GroupKey{ProductType = x.product_type, ProductSize = x.product_size })
                .SelectMany<IGrouping<GroupKey,Price>,Price,Price>(x => x.Where(k => x.Count(y => y.region == 17) == 0 || k.region == 17), (x,g) => g)
                .OrderBy(x=>x.pkey);
于 2013-08-02T00:02:49.000 に答える