LINQ to SQL - Left Outer Join with multiple join conditions
我有以下SQL,我正在尝试将其转换为LINQ:
1 2 3 4 | 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 |
我已经看到了左外部联接的典型实现(即
编辑
为什么
不幸的是:
1 2 3 4 5 | 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 |
似乎等同于:
1 2 3 4 | 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 |
这不是我想要的。
在调用
1 2 3 4 5 | 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 |
或者可以使用子查询:
1 2 3 4 5 6 7 | 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 |
如果您有多个列联接,这也可以工作。
1 2 3 4 5 6 7 8 9 10 11 12 |
我知道这"有点晚了",但是如果有人需要在linq方法语法中这样做(这就是我最初发现这篇文章的原因),这就是如何做到的:
1 2 3 4 5 6 7 8 9 10 11 | 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(); |
另一个有效的选项是将连接分散到多个LINQ子句中,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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; } } |
在我看来,在试图翻译SQL代码之前考虑对其进行一些重写是有价值的。
就我个人而言,我会写一个联合查询(尽管我会完全避免空值!):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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回答的精神(尽管他们的代码似乎与问题无关)。
然而,查询似乎是专门为生成包含重复行的单列结果而设计的——实际上是重复的空值!很难不得出这种方法有缺陷的结论。