【问题标题】:Left join query not working as expected左连接查询未按预期工作
【发布时间】:2011-10-19 06:55:30
【问题描述】:

如何在 Linq to Entities 中编写此查询:

SELECT * FROM TableA a LEFT JOIN TableB b ON a.Id = b.TableAId WHERE ISNULL(b.Id) OR b.FieldA = 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

谢谢, 戈兰

【问题讨论】:

    标签: linq-to-entities entity-framework-4.1


    【解决方案1】:

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

    这将返回带有过滤TableBs 的匿名类型。无法投影到映射实体 (MyParentClass)。

    【讨论】:

    • 嗨Ladislav,正如你在我的第一篇文章中看到的那样,我正在过滤TableB中的数据,y => y.FieldA == 1。过滤TableB数据后,我需要将它与TableA连接起来,以包含 tableA 中的所有记录和 TableB 中的相关数据的方式,但仅应用了过滤器 (y => y.FieldA == 1)。
    • 但是\我需要它是可编辑的,而不是只读的。我不确定我是否理解您为什么说过滤无法过滤关系,如果在第一个查询而不是 .Any() 我放了 .Any(y => y.FieldA == 1) 那么我正在过滤通过关系记录。是我遗漏了什么,还是我不太明白你在说什么?
    • 您只能过滤顶级实体 - 这是 Any 在第一个查询中所做的,但您不能过滤内部关系。这个不允许。如果您需要实体可编辑,您必须加载所有TableBChildren,不加载它们,或者使用单独的查询来加载它们过滤。您还可以在应用程序端使用过滤集合加载投影和重建/重新连接实体。
    猜你喜欢
    • 1970-01-01
    • 2015-06-17
    • 1970-01-01
    • 2012-11-13
    • 2012-03-31
    • 2016-12-24
    • 1970-01-01
    • 1970-01-01
    • 2015-09-05
    相关资源
    最近更新 更多