2013-08-01 3 views
1

Im пытается решить что-то только одним предложением linq, и я не знаю, возможно ли это сделать. У меня есть одна таблица с именем ЦЕН, с этим полем:Слияние двух таблиц с использованием критериев с linq

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

Уникальный ключом является: product_type + product_size

Я хочу, чтобы сделать запрос, который возвращает все строки, в которых область == 17 (это мой первый набор строк) И хочу добавить все строки, где область равна нулю (это мой второй набор строк) BUT , если в обоих наборах есть строки с одинаковыми 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 1 отбрасывают, так как строка с клавишей P. 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; 

Вопросы:

  1. Как присоединиться к запросу1 и query2 для получения ожидаемого результата?

  2. Это может быть сделано только с одним запросом?

ответ

2

После запроса выбирает только цены с регионом 17 или null, группируют их по уникальному ключу { p.product_type, p.product_size }. Затем он проверяет, содержит ли группа по крайней мере одну цену в регионе 17. Если да, то мы выбираем все цены этого региона из группы (и пропуская цены с региона). В противном случае мы возвращаем всю группу (она имеет только нулевые регионы):

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 

EDIT Запрос выше отлично работает с объектами в памяти (т.е. LINQ to Objects), но LINQ to Entitis не так эффективен - он не поддерживает вложенные запросы. Таким образом, для Entity Framework вам потребуется два запрос - один, чтобы принести цены с 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); 

На самом деле EF выполняет как суб -запросы в одном запросе UNION.После SQL будет генерироваться (я удалил DESC и цена столбцы соответствуют экрану):

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 был переведен на внутреннее соединение с условиями.

+1

Вы качается человек, он работал прекрасно – DiegoGalo

+0

я имея исключение с запросом: «Действительный ResultType может не следует выводить из результатов ResultTypes указанных выражений «Then». Почему это происходит? – DiegoGalo

+2

@DiegoGalo Я протестировал запрос 'lazyberezovsky', и он работает нормально. Стиль 'expression' делает этот запрос более кратким, чем стиль' method'. –

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

Приятное использование предложения SelectMany, ваши решения работали очень хорошо – DiegoGalo

+0

Ошибка Аргументы типа для метода 'System.Linq.Enumerable.SelectMany (System.Collections.Generic.IEnumerable , System.Func >, System.Func ) 'не может быть выведен из использования. Попробуйте явно указать аргументы типа. – DiegoGalo

+0

@DiegoGalo Я обновил свой ответ, все 2 пути были протестированы. –