2009-07-14 191 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

编辑

为什么的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

甜!我一直在寻找这一段时间,但不知道如何搜索这个。不知道如何添加标签到这个答案。这里是我使用的搜索条件: LINQ到SQL过滤器在连接或从 LINQ到SQL where子句在连接或从 – Solburn 2010-09-21 14:59:01

回答

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查询中执行整个操作慢? – 2017-06-23 11:16:11

+0

@ umar-t,很可能,考虑到这是八年多前我写的。我个人喜欢Dahlbyk推荐的相关子查询https://stackoverflow.com/a/1123051/212950 – MAbraham1 2017-06-23 17:11:55

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