2009-07-14 3 views
126

У меня есть следующие SQL, которые я пытаюсь перевести LINQ:LINQ к SQL - левое внешнее соединение с несколькими условиями соединения

SELECT f.value 
FROM period as p 
LEFT OUTER JOIN facts AS f ON p.id = f.periodid AND f.otherid = 17 
WHERE p.companyid = 100 

Я видел, типичная реализация левого внешнего соединения (то есть. into x from y in x.DefaultIfEmpty() и т.д.), но не уверен, как ввести другое условие соединения (AND f.otherid = 17)

EDIT

Почему AND f.otherid = 17 Условная часть JOIN вместо предложения WHERE? Поскольку f может не существовать для некоторых строк, и я все же хочу, чтобы эти строки были включены. Если условие применяется в предложении WHERE, после JOIN - тогда я не получаю поведение, которое я хочу.

К сожалению, это:

from p in context.Periods 
join f in context.Facts on p.id equals f.periodid into fg 
from fgi in fg.DefaultIfEmpty() 
where p.companyid == 100 && fgi.otherid == 17 
select f.value 

, как представляется, эквивалентно следующему:

SELECT f.value 
FROM period as p 
LEFT OUTER JOIN facts AS f ON p.id = f.periodid 
WHERE p.companyid = 100 AND f.otherid = 17 

, который не совсем то, что я после.

+0

Sweet!Я искал это некоторое время, но не знал, как это искать. Не знаете, как добавить теги к этому ответу. Вот критерии поиска, которые я использовал: linq to sql filter в join или от linq to sql where clause in join или от – Solburn

ответ

205

Перед тем, как позвонить DefaultIfEmpty(), вам необходимо ввести данные о соединении. Я просто хотел бы использовать метод расширения синтаксиса:

from p in context.Periods 
join f in context.Facts on p.id equals f.periodid into fg 
from fgi in fg.Where(f => f.otherid == 17).DefaultIfEmpty() 
where p.companyid == 100 
select f.value 

Или вы могли бы использовать подзапрос:

from p in context.Periods 
join f in context.Facts on p.id equals f.periodid into fg 
from fgi in (from f in fg 
      where f.otherid == 17 
      select f).DefaultIfEmpty() 
where p.companyid == 100 
select f.value 
5

Другим допустимым вариантом является распространение присоединяется через несколько LINQ статьи следующим образом:

public static IEnumerable<Announcementboard> GetSiteContent(string pageName, DateTime date) 
{ 
    IEnumerable<Announcementboard> content = null; 
    IEnumerable<Announcementboard> addMoreContent = null; 
     try 
     { 
      content = from c in DB.Announcementboards 
       //Can be displayed beginning on this date 
       where c.Displayondate > date.AddDays(-1) 
       //Doesn't Expire or Expires at future date 
       && (c.Displaythrudate == null || c.Displaythrudate > date) 
       //Content is NOT draft, and IS published 
       && c.Isdraft == "N" && c.Publishedon != null 
       orderby c.Sortorder ascending, c.Heading ascending 
       select c; 

      //Get the content specific to page names 
      if (!string.IsNullOrEmpty(pageName)) 
      { 
       addMoreContent = from c in content 
        join p in DB.Announceonpages on c.Announcementid equals p.Announcementid 
        join s in DB.Apppagenames on p.Apppagenameid equals s.Apppagenameid 
        where s.Apppageref.ToLower() == pageName.ToLower() 
        select c; 
      } 

      //CROSS-JOIN this content 
      content = content.Union(addMoreContent); 

      //Exclude dupes - effectively OUTER JOIN 
      content = content.Distinct(); 

      return content; 
     } 
    catch (MyLovelyException ex) 
    { 
     throw ex; 
    } 
} 
+0

не было бы медленнее, чем выполнение всей операции в одном запросе linq? –

+0

@ umar-t, Да, скорее всего, учитывая, что это было более восьми лет назад, когда я его написал. Лично мне нравится коррелированный подзапрос, постулированный Далбыком здесь https://stackoverflow.com/a/1123051/212950 – MAbraham1

23

это тоже работает, если у вас есть несколько столкновений столбцов

from p in context.Periods 
join f in context.Facts 
on new { 
    id = p.periodid, 
    p.otherid 
} equals new { 
    f.id, 
    f.otherid 
} into fg 
from fgi in fg.DefaultIfEmpty() 
where p.companyid == 100 
select f.value 
-1

Мне кажется, что есть смысл рассматривать некоторые перезаписи для вашего кода SQL, прежде чем пытаться его перевести.

Лично я бы написать такой запрос, как союз (хотя я бы избежать аннулирует полностью!):

SELECT f.value 
    FROM period as p JOIN facts AS f ON p.id = f.periodid 
WHERE p.companyid = 100 
     AND f.otherid = 17 
UNION 
SELECT NULL AS value 
    FROM period as p 
WHERE p.companyid = 100 
     AND NOT EXISTS ( 
         SELECT * 
         FROM facts AS f 
         WHERE p.id = f.periodid 
          AND f.otherid = 17 
        ); 

Так что я предполагаю, что я согласен с духом @ MAbraham1 отвечают (хотя их код кажется, не имеет отношения к вопросу).

Однако, похоже, что запрос явно предназначен для получения единственного результата столбца, содержащего повторяющиеся строки - действительно дублирующие нули! Трудно не прийти к выводу, что этот подход ошибочен.

7

Я знаю, что это «немного поздно», но только в том случае, если кто-то должен сделать это в LINQ метод синтаксиса (, поэтому я нашел этот пост изначально), это было бы, как это сделать :

var results = context.Periods 
    .GroupJoin(
     context.Facts, 
     period => period.id, 
     fk => fk.periodid, 
     (period, fact) => fact.Where(f => f.otherid == 17) 
           .Select(fact.Value) 
           .DefaultIfEmpty() 
    ) 
    .Where(period.companyid==100) 
    .SelectMany(fact=>fact).ToList(); 
+0

Очень полезно увидеть лямбду-версию! – Learner

Смежные вопросы