我目前正在使用带有SQL Server的EFCore 1.1(预览版)。
我正在做我认为是Order
和OrderItem
表之间的简单外部联接。
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很糟糕,包括Order
和OrderItem
中的每一列,如果其中一列是一个大的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中已得到修复。
答案 0 :(得分:8)
以下内容适用于EF Core 1.1.0(发行版)。
虽然不应该这样做,但尝试了几种替代语法查询(使用导航属性而不是手动连接,使用let
/中间Select
加入包含匿名类型投影的子查询,使用Concat
/ Union
模拟左连接,替代左连接语法等。)结果 - 与post中的相同,和/或执行多个查询,和/或无效的SQL查询,和/或奇怪的运行时异常,如IndexOutOfRange
,InvalidArgument
等。
根据测试我可以说的是,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)
使用"进入"将创建一个临时标识符来存储结果。
删除" POST: 404 Not found
"将导致带有三列的干净sql。
into tmp from oi in tmp.DefaultIfEmpty()