EFCore为简单的LEFT OUTER连接返回了太多列

时间:2016-11-11 09:15:32

标签: entity-framework entity-framework-core

我目前正在使用带有SQL Server的EFCore 1.1(预览版)。

我正在做我认为是OrderOrderItem表之间的简单外部联接。

      var orders = from order in ctx.Order
                   join orderItem in ctx.OrderItem

                   on order.OrderId equals orderItem.OrderId into tmp

                   from oi in tmp.DefaultIfEmpty()

                   select new
                   {
                       order.OrderDt,
                       Sku = (oi == null) ? null : oi.Sku,
                       Qty = (oi == null) ? (int?) null : oi.Qty
                   };

返回的实际数据是正确的(我知道早期版本的OUTER JOINS根本无法正常工作)。然而,SQL很糟糕,包括OrderOrderItem中的每一列,如果其中一列是一个大的XML Blob,则会出现问题。

  

SELECT [order]。[OrderId],[order]。[OrderStatusTypeId],   [order]。[OrderSummary],[order]。[OrderTotal],[order]。[OrderTypeId],   [order]。[ParentFSPId],[order]。[ParentOrderId],   [order]。[PayPalECToken],[order]。[PaymentFailureTypeId] ....

     

... [orderItem]。[OrderId],[orderItem]。[OrderItemType],[orderItem]。[数量],   [orderItem]。[SKU] FROM [Order] AS [order] LEFT JOIN [OrderItem] AS   [orderItem] ON [order]。[OrderId] = [orderItem]。[OrderId] ORDER BY   [指令]。[单编号]

(此处未显示更多列。)

另一方面 - 如果我将它设为INNER JOIN,那么SQL就像预期的那样只有我的select子句中的列:

  

SELECT [order]。[OrderDt],[orderItem]。[SKU],[orderItem]。[Qty] FROM   [订单] AS [订单] INNER JOIN [OrderItem] AS [orderItem] ON   [order]。[OrderId] = [orderItem]。[OrderId]

我尝试恢复到EFCore 1.01,但得到了一些可怕的nuget包错误并放弃了。

不清楚这是EFCore中的实际回归问题还是不完整的功能。但在其他地方找不到任何关于此的更多信息。

编辑:EFCore 2.1解决了很多分组问题以及N + 1类型问题,其中为每个子实体进行单独查询。实际上对性能印象非常深刻。

3/14/18 - 2.1建议不要使用EFCore预览1,因为GROUP BY SQL在使用OrderBy()时会出现一些问题,但它在夜间构建和预览2中已得到修复。

2 个答案:

答案 0 :(得分:8)

以下内容适用于EF Core 1.1.0(发行版)。

虽然不应该这样做,但尝试了几种替代语法查询(使用导航属性而不是手动连接,使用let /中间Select加入包含匿名类型投影的子查询,使用Concat / Union模拟左连接,替代左连接语法等。)结果 - 与post中的相同,和/或执行多个查询,和/或无效的SQL查询,和/或奇怪的运行时异常,如IndexOutOfRangeInvalidArgument等。

根据测试我可以说的是,GroupJoin翻译中的问题很可能与bug(回归,不完整的实现 - 确实很重要)有关。例如,#7003: Wrong SQL generated for query with group join on a subquery that is not present in the final projection#6647 - Left Join (GroupJoin) always materializes elements resulting in unnecessary data pulling

直到它被修复(何时?),作为一种(远非完美的)解决方法,我可以建议使用替代的左外连接语法(from a in A from b in B.Where(b = b.Key == a.Key).DefaultIfEmpty()):

var orders = from o in ctx.Order
             from oi in ctx.OrderItem.Where(oi => oi.OrderId == o.OrderId).DefaultIfEmpty()
             select new
             {
                 OrderDt = o.OrderDt,
                 Sku = oi.Sku,
                 Qty = (int?)oi.Qty
             };

生成以下SQL:

SELECT [o].[OrderDt], [t1].[Sku], [t1].[Qty]
FROM [Order] AS [o]
CROSS APPLY (
    SELECT [t0].*
    FROM (
        SELECT NULL AS [empty]
    ) AS [empty0]
    LEFT JOIN (
        SELECT [oi0].*
        FROM [OrderItem] AS [oi0]
        WHERE [oi0].[OrderId] = [o].[OrderId]
    ) AS [t0] ON 1 = 1
) AS [t1]

正如您所看到的,投影是正常的,但它使用了奇怪的LEFT JOIN而不是CROSS APPLY,这可能会引入另一个性能问题。

另请注意,在访问右连接表时,必须使用强制类型转换,而不必使用任何字符串,如上所示。如果您在原始查询中使用null检查,那么您将在运行时获得ArgumentNullException(还有另一个错误)。

答案 1 :(得分:-2)

使用"进入"将创建一个临时标识符来存储结果。

参考:MDSN: into (C# Reference)

删除" POST: 404 Not found "将导致带有三列的干净sql。

into tmp from oi in tmp.DefaultIfEmpty()