Linq-to-Entities:带有 WHERE 子句和投影的 LEFT OUTER JOIN
Posted
技术标签:
【中文标题】Linq-to-Entities:带有 WHERE 子句和投影的 LEFT OUTER JOIN【英文标题】:Linq-to-Entities: LEFT OUTER JOIN with WHERE clause and projection 【发布时间】:2011-04-26 23:24:10 【问题描述】:我花了很长时间弄清楚如何将带有两个条件 where 子句的简单 SQL LEFT OUTER JOIN 转换为有效的 Linq-to-Entities 查询。只有两张桌子。我需要 Table1 中所有行的值,无论 Table2 中的匹配项如何,但 WHERE 子句使用 Table2 中的字段。在 SQL 中,这两个参数是 Table2WhereColumn1 和 Table2WhereColumn2,查询(有效)如下所示:
SELECT t1.Table1Id,
t1.FieldDescription,
t2.FieldValue
FROM Table1 t1 WITH (NOLOCK)
LEFT JOIN Table2 t2 WITH (NOLOCK) ON t1.Table1Id = t2.Table1Id
WHERE (t2.Table2WhereColumn1 = @someId OR t2.Table2WhereColumn1 IS NULL)
AND (t2.Table2WhereColumn2 = @someOtherId OR t2.Table2WhereColumn2 IS NULL)
ORDER BY t1.OrderByColumn
我尝试过将Group Join
与DefaultIfEmpty()
一起使用,以及隐式连接(没有实际的Join
关键字),但我只获取表2 中具有值的项目的行。我确信这不会有帮助,但这是我一直在尝试但不起作用的 Linq 示例:
Public Shared Function GetProfilePreferencesForCedent(ByVal dc As EntityContext, _
ByVal where1 As Int32, _
ByVal where2 As Int32) _
As IQueryable(Of ProjectedEntity)
Return From t1 In dc.Table1
Group Join t2 In dc.Table2 _
On t1.Table1Id Equals t2.Table1Id _
Into t2g1 = Group _
From t2gx In t2g1.DefaultIfEmpty(Nothing)
Where (t2gx.Table2Where1 = where1 Or t2gx.Table2Where1 = Nothing) _
And (t2gx.Table2Where2 = where2 Or t2gx.Table2Where2 = Nothing)
Order By t1.SortOrder
Select New ProjectedEntity With
.Table1Id = t1.Table1Id, _
.FieldDescription = t1.FieldDescription, _
.FieldValue = If(t2gx Is Nothing, String.Empty, t2gx.FieldValue) _
End Function
【问题讨论】:
【参考方案1】:试试这些查询,告诉我它们是否适合您。我还没有设置要测试的数据,但它们应该没问题。
请原谅我混合使用 C# 和 VB.NET。我曾经是一名 VB.NET 开发人员,但在过去的几年中,我主要使用 C# 工作,所以我现在在那里感觉更舒服了。
这是我为Table1
& Table2
创建的类:
public class Table1
public int Table1Id get; set;
public string FieldDescription get; set;
public int OrderByColumn get; set;
public class Table2
public int Table1Id get; set;
public string FieldValue get; set;
public int Table2WhereColumn1 get; set;
public int Table2WhereColumn2 get; set;
现在 C# 中的查询应该是:
var query =
from t1 in Table1
join t2 in Table2 on t1.Table1Id equals t2.Table1Id into _Table2
from _t2 in _Table2.DefaultIfEmpty()
where _t2 == null ? true :
_t2.Table2WhereColumn1 == @someId
&& _t2.Table2WhereColumn2 == @someOtherId
orderby t1.OrderByColumn
select new
t1.Table1Id,
t1.FieldDescription,
FieldValue = _t2 == null ? "" : _t2.FieldValue,
;
并翻译成VB.NET:
Dim query = _
From t1 In Table1 _
Group Join t2 In Table2 On t1.Table1Id Equals t2.Table1Id Into _Table2 = Group _
From _t2 In _Table2.DefaultIfEmpty() _
Where If(_t2 Is Nothing, True, _t2.Table2WhereColumn1 = someId AndAlso _
_t2.Table2WhereColumn2 = someOtherId) _
Order By t1.OrderByColumn _
Select New With _
.Table1Id = t1.Table1Id, _
.FieldDescription = t1.FieldDescription, _
.FieldValue = If(_t2 Is Nothing, "", _t2.FieldValue) _
让我知道它们是否有效。手指交叉。 :-)
【讨论】:
优秀!奇迹般有效。我不确定我是否理解 Where 子句的工作原理,但它确实有效。【参考方案2】:就个人而言,如果存在左连接右侧的 where 条件,我通常更喜欢将它们放入连接条件中
在这种情况下,SQL 如下所示:
SELECT t1.Table1Id,
t1.FieldDescription,
t2.FieldValue
FROM Table1 t1 WITH (NOLOCK)
LEFT JOIN Table2 t2 WITH (NOLOCK) ON t1.Table1Id = t2.Table1Id
AND t2.Table2WhereColumn1 = @someId
AND t2.Table2WhereColumn2 = @someOtherId
ORDER BY t1.OrderByColumn
用于此的 LINQ 代码(在 C# 中)如下所示:
var query =
from t1 in Table1
join t2 in Table2 on newa = t1.Table1Id, b = someId, c = someotherId
equals new a = t2.Table1Id b = t2.Table2WhereColumn1, c = Table2WhereColumn2
into _Table2
from _t2 in _Table2.DefaultIfEmpty()
orderby t1.OrderByColumn
select new
t1.Table1Id,
t1.FieldDescription,
FieldValue = _t2 == null ? "" : _t2.FieldValue,
;
未测试 - 但应该可以工作
【讨论】:
+1 这是我喜欢生成 Linq 右表的方式,其中左外连接上的条件我觉得它读起来更类似于我习惯的 Sql 语法。 语法真的很好。我必须同意更像 SQL 的感觉。 如果在 JOIN 中使用 OR 条件,您如何执行上述操作?【参考方案3】:我不会把这个答案归功于这个答案,但它很漂亮:LINQ to SQL - Left Outer Join with multiple join conditions
本质上,在子查询上使用扩展方法 where 子句,但必须在 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
【讨论】:
简洁大方,完美。以上是关于Linq-to-Entities:带有 WHERE 子句和投影的 LEFT OUTER JOIN的主要内容,如果未能解决你的问题,请参考以下文章
分组后的LINQ to Entities,COUNT和WHERE不起作用
为啥 LINQ-to-Entities 将此查询放在子选择中?