Linq – левое соединение в нескольких (OR) условиях

Мне нужно сделать левое соединение на нескольких условиях, где условия OR а не AND s. Я нашел множество образцов последнего, но изо всех сил пытаюсь получить правильный ответ для моего сценария.

 from a in tablea join b in tableb on new { a.col1, a.col2 } equals new { b.col1, b.col2 } group a by a into g select new () { col1 = a.col1, col2 = a.col2, count = g.Count() } 

отлично подходит для объединений, где должны соответствовать все условия. Мне нужно, чтобы соединение on a.col1 = b.col1 OR a.col2 = b.col2 .

Я знаю, что это должно быть легко, но я все время боюсь!

Редактировать:

Чтобы дать немного больше информации, цель запроса состоит в том, чтобы получить проекцию, содержащую все поля из «a», плюс количество совпадающих записей в «b». Я изменил пример выше, чтобы попытаться проиллюстрировать, что мне нужно. Когда я запускаю с помощью вышеописанного подхода, Джон Скит отметил, что я получаю счет всех записей от a, а не счет связанных записей в b.

Основное левое соединение отлично работает:

 from a in tablea from b in tableb .Where( b => ( a.col1 == b.col1 || a.col2 == b.col2)) .DefaultIfEmpty() select new { col1 = a.col1, col2 = a.col2 } 

Если я пересмотрю его, чтобы добавить группировку, как показано ниже

 from a in tablea from b in tableb .Where( b => ( a.col1 == b.col1 || a.col2 == b.col2)) .DefaultIfEmpty() group a by a.col1 into g select new { col1 = g.Key, count = g.Count() } 

Я получаю подсчет записей, возвращаемых с a, а не количество записей, совпадающих по b.

Редактировать:

Я дам ответ Джону – я решил проблему с подсчетом – я не понял, что могу использовать ламду для фильтрации счета (g.Count(x => x != null)) . Кроме того, мне нужно группировать букву «а», а не «а», как было выше. Это дает правильный результат, но SQL не так эффективен, как я бы написал его вручную, поскольку он добавляет коррелированный подзапрос – если кто-нибудь может посоветовать лучший способ написать его для моделирования следующего SQL, я бы его оценил!

 select a.col1, count(b.col1) from tablea a left join tableb b on a.col1 = b.col1 or a.col2 = b.col2 group by a.col1 

LINQ напрямую поддерживает equijoins. Если вы хотите сделать какой-либо другой вид соединения, вам в основном нужно перекрестное соединение и where :

 from a in tablea from b in tableb where a.col1 == b.col1 || a.col2 == b.col2 select ... 

Вероятно, стоит проверить, как выглядит сгенерированный SQL и как выглядит план запроса. Там могут быть более эффективные способы сделать это, но это, вероятно, самый простой подход.

В зависимости от поставщика запроса вы можете просто выбрать два из предложений:

 from a in tablea from b in tableb where a.col1 == b.col1 || a.col2 == b.col2 

Который, если вы выполните на БД, будет таким же эффективным. Если вы выполняете встроенную память (Linq to Objects), это будет перечислять все возможные комбинации, которые могут быть неэффективными.

Arg, Skeeted ;-).

Возможны более эффективные альтернативы Linq to Objects. Оператор join перечисляет каждый источник только один раз, а затем выполняет хеш-соединение, поэтому вы можете разделить or-clause на два отдельных объединения и затем принять их объединение. Объединение в linq – это просто конкатенация без дубликатов, так что это выглядит следующим образом:

 (from a in tablea join b in tableb on a.Col1 equals b.Col1 select new {a, b}) .Concat( from a in tablea join b in tableb on a.Col2 equals b.Col2 select new {a, b} ).Distinct() 

Этот подход работает, и это всего лишь один запрос, но он несколько неочевиден в том смысле, что характеристики производительности кода зависят от детального понимания того, как работает linq. Лично, если вы хотите сделать хеш-соединение с потенциально множественными совпадениями, более очевидным инструментом является ToLookup . Альтернативный вариант, который может выглядеть следующим образом:

 var bBy1 = tableb.ToLookup(b=>b.Col1); var bBy2 = tableb.ToLookup(b=>b.Col2); var q3 = from a in tablea from b in bBy1[a.Col1].Concat(bBy2[a.Col2]).Distinct() ... 

Это решение на самом деле короче, и причина, по которой он работает, более очевидна, поэтому я предпочту. Просто помните, что если вы разделите || оператор в два отдельных запроса, например, в двух вышеупомянутых сценариях, которые вам нужно вручную избежать двойного подсчета результатов (т. е. использовать Distinct ).

  • Несколько / одиночный экземпляр Linq to SQL DataContext
  • LINQ to SQL с использованием GROUP BY и COUNT (DISTINCT)
  • LINQ to SQL: несколько объединений в нескольких столбцах. Это возможно?
  • Как выполнить подзапрос в LINQ?
  • Есть ли шаблон, использующий Linq для динамического создания фильтра?
  • Лучшая практика: преобразование результата запроса LINQ в DataTable без цикла
  • Как сделать левое внешнее соединение с помощью Dynamic Linq?
  • Общее выражение таблицы (CTE) в linq-to-sql?
  • LINQ для нескольких баз данных
  • Я не понимаю LINQ to SQL .AsEnumerable ()?
  • ASP.MVC: repository, который отражает IQueryable, но не Linq to SQL, DDD. Как поставить вопрос
  • Давайте будем гением компьютера.