2011-10-19 104 views
0

如何写这个查询在LINQ到实体:LEFT JOIN查询工作不正常

SELECT * FROM TableA a LEFT JOIN TableB b ON a.Id = b.TableAId WHERE ISNULL(b.Id) OR b.FieldA = 1 

关系是1对多,但限制b.FIeldA = 1是确保实际数据返回的结果为1-0 ... 1,这意味着返回记录的数量应该等于TableA中的记录。我需要从TableA获取所有数据,并从TableB中加入数据。我试图用这个查询,其实际上像INNER JOIN一样预先形成(来自TableA的记录没有在TableB中的相关记录未被检索到)

Vladislav建议查询下面,我已经在第一个查询中添加了额外的过滤:

var query = (from x in myParentClasses.Include(x => x.TableBChildren) 
     where !x.TableBChildren.Any(y => y.FieldA == 1) 
     select x) 
     .Concat(
     from x in myParentClasses.Include(x => x.TableBChildren) 
     where x.TableBChildren.Any(y => y.FieldA == 1 || y.Id == null) 
     select x) 
     .ToList(); 

生成的SQL是这样的:

SELECT [Project1][...] 
FROM  (SELECT [Extent1].[...], 
       [Extent2].[...], 
       [Extent3].[...], 
       CASE 
        WHEN ([Extent3].[Id] IS NULL) THEN CAST(NULL AS int) 
        ELSE 1 
       END AS [C1] 
      FROM [dbo].[ParentTable] AS [Extent1] 
       LEFT OUTER JOIN [dbo].[ReferenceTable] AS [Extent2] 
        ON [Extent1].[ReferenceId] = [Extent2].[Id] 
       LEFT OUTER JOIN [dbo].[ChildrenTable] AS [Extent3] 
        ON [Extent1].[Id] = [Extent3].[ParentId] 
      WHERE [Extent1].[FieldA] = 1 /* @p__linq__0 */) AS [Project1] 
ORDER BY [Project1].[Id] ASC, 
     [Project1].[Id1] ASC, 
     [Project1].[C1] ASC 

谢谢, 戈兰

回答

0

Linq-to-entities与SQL完全不一样。只有在查询允许的情况下,它才执行左连接(除非手动编写连接,否则无法手动控制该行为)。这不是您的查询的情况,因为它使用x.TableBChildren.Any =它要求父表具有满足条件的子表的任何记录,因此它使用内连接。

我没有尝试,有可能是更好的方式,但我认为这可能是工作:

var query = (from x in myParentClasses.Include(x => x.TableBChildren) 
      where !x.TableBChildren.Any() 
      select x) 
      .Concat(
      from x in myParentClasses.Include(x => x.TableBChildren) 
      where x.TableBChildren.Any(y => y.FieldA == 1 || y.Id == null) 
      select x) 
      .ToList(); 

第一部分选择不具有相关实体和第二部分中的所有记录选择具有相关实体记录满足你的条件。它使这两部分结合。

另外请注意,Include不会过滤您的关系 - 它会每次选择所有关系。如果您想要过滤它们,则必须使用投影编写查询。

编辑:

如果你想有TableB过滤您不能正常使用的查询和Include。您也无法返回MyParentClass的实例。您必须使用投影:

var query = from x in myParentClasses 
      select new 
       { 
        Id = x.Id, 
        // Rest of properties from myParentClass you want to receive 
        TableBChildren = x.TableBChildren.Where(y => y.FieldA == 1 || y.Id == null) 
       }; 

这将返回匿名类型与过滤TableB秒。不可能投影到映射实体(MyParentClass)。

+0

Hi Ladislav, 正如你可以在我的第一篇文章中看到的,我在TableB中过滤数据,y => y.FieldA == 1.在过滤TableB数据之后,我需要用TableA加入它,包含tableA的所有记录和来自TableB的相关数据,但只适用滤波器(y => y.FieldA == 1)。 – Goran

+0

但\我需要它作为可编辑的,而不是只读的。我不知道我明白你为什么说过滤不可能过滤关系,如果在第一个查询而不是.Any()我把我放.Any(y => y.FieldA == 1)然后我过滤通过关系记录。 我在这里错过了什么,或者我不太明白你在说什么? – Goran

+0

您只能过滤顶级实体 - 这是第一个查询中的任何操作,但不能过滤内部关系。不允许。如果你需要实体可编辑,你必须加载所有'TableBChildren',而不是它们或使用单独的查询来加载它们过滤。您还可以在应用程序端使用已过滤的集合加载投影和重建/重新绑定实体。 –