简体   繁体   English

使用linq标准合并两个表

[英]Merging two tables using criteria with linq

Im trying to resolve something with just one linq sentence, and I dont know if is possible do this. 我试图用一个linq句子来解决问题,我不知道是否有可能做到这一点。 I have one table named PRICES, with this fields: 我有一个名为PRICES的表,其中包含以下字段:

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

The unique key is: product_type + product_size 唯一键是:product_type + product_size

I want to do a query that returns all rows WHERE region == 17 (this is my first set of rows) AND want to add all rows where region is null (this is my second set of rows) BUT if there are rows with the same product_type and product_size in both sets, i want in the final result just the row of the first set. 我想做一个返回所有行的查询WHERE region == 17(这是我的第一组行)并且想要添加region为null的所有行(这是我的第二组行)但是如果有行包含两组中的product_type和product_size相同,我想在最终结果中只是第一组的行。

Example: 例:

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

I want a linq query that returns this: 我想要一个返回这个的linq查询:

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

(note that row with pkey 1 is discarded because the row with pkey 3 has the same product_type and product_size) (注意,丢弃带有pkey 1的行,因为pkey 3的行具有相同的product_type和product_size)

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;

Questions: 问题:

  1. How to join query1 and query2 to obtain the expected output? 如何加入query1和query2来获得预期的输出?

  2. It can be done with just 1 query? 只需1个查询即可完成?

Following query selects only prices with region 17 or null , groups them by unique key { p.product_type, p.product_size } . 以下查询仅选择区域17null价格,按唯一键{ p.product_type, p.product_size }它们进行{ p.product_type, p.product_size } Then it checks whether group contain at least one price with region 17 . 然后它检查组是否包含至少一个区域17价格。 If yes, then we select all prices of this region from group (and skipping prices with null region). 如果是,那么我们从组中选择该区域的所有价格(并跳过具有null区域的价格)。 Otherwise we return whole group (it has null regions only): 否则我们返回整个组(它只有空区域):

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;

Input: 输入:

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}

Output: 输出:

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

EDIT Query above works fine with objects in memory (ie LINQ to Objects) but LINQ to Entitis is not that powerful - it does not support nested queries. 上面的EDIT查询可以很好地处理内存中的对象(即LINQ to Objects),但LINQ to Entitis不是那么强大 - 它不支持嵌套查询。 So, for Entity Framework you will need two queries - one to fetch prices with null region, which does not have region 17 prices in the group, and second - prices from region 17 : 因此,对于实体框架,您将需要两个查询 - 一个用于获取具有null区域的价格,该区域中没有区域17价格,第二个 - 来自区域17价格:

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);

Actually EF executes both sub-queries in one UNION query to server. 实际上,EF在一个UNION查询服务器中执行两个子查询。 Following SQL will be generated (I removed desc and price columns to fit screen): 将生成以下SQL(我删除了descprice列以适应屏幕):

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]

BTW it's surprise to me that GroupBy was translated into inner join with conditions. 顺便说一下,我很惊讶GroupBy被翻译成具有条件的内连接。

I think you should go for 1 query, for 2 queries, we have to repeat something: 我认为你应该进行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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM