为什么这个EF查询产生如此疯狂的生成SQL?

时间:2018-05-31 20:33:04

标签: sql performance entity-framework database-performance

var statusId = db.WorkOrder.Where(w => w.OrderType.Name == "ReadAudit" && w.WorkOrderMapping.MeterOldTag == meterTag && w.OrderStatusId != 80)
                .OrderByDescending(w => w.CreationDatetime)
                .Select(r => r.OrderStatusId)                    
                .FirstOrDefault();

这产生了这个疯狂的SQL:

SELECT TOP (1) 
[Project1].[OrderStatusId] AS [OrderStatusId]
FROM ( SELECT 
    [Filter1].[OrderStatusId] AS [OrderStatusId], 
    [Filter1].[CreationDatetime] AS [CreationDatetime]
    FROM ( 
        SELECT [Extent1].[OrderStatusId] AS [OrderStatusId], 
               [Extent1].[CreationDatetime] AS [CreationDatetime], 
               [Extent3].[MeterOldTag] AS [MeterOldTag]
        FROM   [dbo].[WorkOrder] AS [Extent1]
        INNER JOIN [dbo].[OrderType] AS [Extent2] ON [Extent1].[OrderTypeKey] = [Extent2].[OrderTypeKey]
        LEFT OUTER JOIN [dbo].[WorkOrderMapping] AS [Extent3] ON [Extent1].[WorkOrderKey] = [Extent3].[WorkOrderMappingKey]
        WHERE (80 <> [Extent1].[OrderStatusId]) 
        AND (N'ReadAudit' = [Extent2].[Name])
    )  AS [Filter1]
    WHERE ([Filter1].[MeterOldTag] = @p__linq__0) OR (([Filter1].[MeterOldTag] IS NULL) AND (@p__linq__0 IS NULL))
)  AS [Project1]
ORDER BY [Project1].[CreationDatetime] DESC

我告诉说它很难打到数据库:

表&#39; WorkOrder&#39;。扫描计数30,逻辑读取84403

表&#39; WorkOrderMapping&#39;。扫描计数9,逻辑读取16516

EF查询看起来并不复杂。有没有办法让生成的SQL更有效?

2 个答案:

答案 0 :(得分:0)

这就是实体框架的工作原理。

如果您想要更多控制权,可以通过Entity Framework Raw Queries自己编写查询,请参阅here以及(不完整)示例下面的查询结果。

DbRawSqlQuery<Int32> query = db.Database.SqlQuery<Int32>("SELECT OrderStatusId FROM ... ");
var statusId = query.FirstOrDefault();

修改

另请参阅为此查询执行的实际查询计划,以查找相关表中是否存在相应的索引。

考虑将常量RealAudit80移动到变量,如下所示:

var orderType = "RealAudit";
var orderStatusId = 80;

var statusId = db.WorkOrder.Where(w => w.OrderType.Name == orderType
        && w.WorkOrderMapping.MeterOldTag == meterTag 
        && w.OrderStatusId != orderStatusId
    )
    .OrderByDescending(w => w.CreationDatetime)
    .Select(r => r.OrderStatusId)                    
    .FirstOrDefault();

通过这样做,这些将在查询中显示为SQL参数,如:

@p__linq__1 <> [Extent1].[OrderStatusId]) 
AND (@p__linq__2 = [Extent2].[Name])

这允许单个查询计划可以用于此查询的所有变体,而现在您可以获得MeterOldTag参数的单独值的查询计划。

答案 1 :(得分:0)

关于SQL Query的唯一“疯狂”是关于MeterOldTag的谓词。它是这样编写的,因为默认情况下,EF会编写查询来模拟LINQ查询的C#比较语义。如果要在数据库中进行简单的相等比较,请为DbContext设置UseDatabaseNullSemantics