[英]Options to replace slow SQL generated by Entity Framework 6
我一直在使用Entity Framework 6代码对我的域模型进行一些简单的CRUD操作,到目前为止,它的执行效果令人赞叹。
现在,我遇到了一种情况,我正在执行相当复杂的查询,其中涉及对结果的过滤和分页。 由EF 6生成的查询与我可以手动处理的查询相比非常糟糕,效率非常低下。 这是生成的SQL,它在约17秒内执行:
SELECT TOP ( 15 )
[Project1].[Branch] AS [Branch] ,
[Project1].[Salesman] AS [Salesman] ,
[Project1].[Status] AS [Status] ,
[Project1].[OrderID] AS [OrderID] ,
[Project1].[DateCreated] AS [DateCreated] ,
[Project1].[DateCompleted] AS [DateCompleted] ,
[Project1].[RegNumber] AS [RegNumber] ,
[Project1].[Make] AS [Make] ,
[Project1].[Model] AS [Model] ,
[Project1].[Spec] AS [Spec] ,
[Project1].[Title] AS [Title] ,
[Project1].[Firstname] AS [Firstname] ,
[Project1].[Surname] AS [Surname] ,
[Project1].[Address1] AS [Address1] ,
[Project1].[Address2] AS [Address2] ,
[Project1].[Address3] AS [Address3] ,
[Project1].[Town] AS [Town] ,
[Project1].[County] AS [County] ,
[Project1].[Postcode] AS [Postcode] ,
[Project1].[HomePhone] AS [HomePhone] ,
[Project1].[WorkPhone] AS [WorkPhone] ,
[Project1].[MobilePhone] AS [MobilePhone] ,
[Project1].[EMailAddress] AS [EMailAddress] ,
[Project1].[AllowMarketing] AS [AllowMarketing] ,
[Project1].[Manager] AS [Manager] ,
[Project1].[FK_BranchID] AS [FK_BranchID]
FROM ( SELECT [Project1].[Branch] AS [Branch] ,
[Project1].[Salesman] AS [Salesman] ,
[Project1].[Status] AS [Status] ,
[Project1].[OrderID] AS [OrderID] ,
[Project1].[DateCreated] AS [DateCreated] ,
[Project1].[DateCompleted] AS [DateCompleted] ,
[Project1].[RegNumber] AS [RegNumber] ,
[Project1].[Make] AS [Make] ,
[Project1].[Model] AS [Model] ,
[Project1].[Spec] AS [Spec] ,
[Project1].[Title] AS [Title] ,
[Project1].[Firstname] AS [Firstname] ,
[Project1].[Surname] AS [Surname] ,
[Project1].[Address1] AS [Address1] ,
[Project1].[Address2] AS [Address2] ,
[Project1].[Address3] AS [Address3] ,
[Project1].[Town] AS [Town] ,
[Project1].[County] AS [County] ,
[Project1].[Postcode] AS [Postcode] ,
[Project1].[HomePhone] AS [HomePhone] ,
[Project1].[WorkPhone] AS [WorkPhone] ,
[Project1].[MobilePhone] AS [MobilePhone] ,
[Project1].[EMailAddress] AS [EMailAddress] ,
[Project1].[AllowMarketing] AS [AllowMarketing] ,
[Project1].[Manager] AS [Manager] ,
[Project1].[FK_BranchID] AS [FK_BranchID] ,
ROW_NUMBER() OVER ( ORDER BY [Project1].[DateCreated] DESC ) AS [row_number]
FROM ( SELECT [Extent1].[Branch] AS [Branch] ,
[Extent1].[Salesman] AS [Salesman] ,
[Extent1].[Status] AS [Status] ,
[Extent1].[OrderID] AS [OrderID] ,
[Extent1].[DateCreated] AS [DateCreated] ,
[Extent1].[DateCompleted] AS [DateCompleted] ,
[Extent1].[RegNumber] AS [RegNumber] ,
[Extent1].[Make] AS [Make] ,
[Extent1].[Model] AS [Model] ,
[Extent1].[Spec] AS [Spec] ,
[Extent1].[Title] AS [Title] ,
[Extent1].[Firstname] AS [Firstname] ,
[Extent1].[Surname] AS [Surname] ,
[Extent1].[Address1] AS [Address1] ,
[Extent1].[Address2] AS [Address2] ,
[Extent1].[Address3] AS [Address3] ,
[Extent1].[Town] AS [Town] ,
[Extent1].[County] AS [County] ,
[Extent1].[Postcode] AS [Postcode] ,
[Extent1].[HomePhone] AS [HomePhone] ,
[Extent1].[WorkPhone] AS [WorkPhone] ,
[Extent1].[MobilePhone] AS [MobilePhone] ,
[Extent1].[EMailAddress] AS [EMailAddress] ,
[Extent1].[AllowMarketing] AS [AllowMarketing] ,
[Extent1].[Manager] AS [Manager] ,
[Extent1].[FK_BranchID] AS [FK_BranchID]
FROM ( SELECT [vw_CS_OrderDetails].[Branch] AS [Branch] ,
[vw_CS_OrderDetails].[Salesman] AS [Salesman] ,
[vw_CS_OrderDetails].[Status] AS [Status] ,
[vw_CS_OrderDetails].[OrderID] AS [OrderID] ,
[vw_CS_OrderDetails].[DateCreated] AS [DateCreated] ,
[vw_CS_OrderDetails].[DateCompleted] AS [DateCompleted] ,
[vw_CS_OrderDetails].[RegNumber] AS [RegNumber] ,
[vw_CS_OrderDetails].[Make] AS [Make] ,
[vw_CS_OrderDetails].[Model] AS [Model] ,
[vw_CS_OrderDetails].[Spec] AS [Spec] ,
[vw_CS_OrderDetails].[Title] AS [Title] ,
[vw_CS_OrderDetails].[Firstname] AS [Firstname] ,
[vw_CS_OrderDetails].[Surname] AS [Surname] ,
[vw_CS_OrderDetails].[Address1] AS [Address1] ,
[vw_CS_OrderDetails].[Address2] AS [Address2] ,
[vw_CS_OrderDetails].[Address3] AS [Address3] ,
[vw_CS_OrderDetails].[Town] AS [Town] ,
[vw_CS_OrderDetails].[County] AS [County] ,
[vw_CS_OrderDetails].[Postcode] AS [Postcode] ,
[vw_CS_OrderDetails].[HomePhone] AS [HomePhone] ,
[vw_CS_OrderDetails].[WorkPhone] AS [WorkPhone] ,
[vw_CS_OrderDetails].[MobilePhone] AS [MobilePhone] ,
[vw_CS_OrderDetails].[EMailAddress] AS [EMailAddress] ,
[vw_CS_OrderDetails].[AllowMarketing] AS [AllowMarketing] ,
[vw_CS_OrderDetails].[Manager] AS [Manager] ,
[vw_CS_OrderDetails].[FK_BranchID] AS [FK_BranchID]
FROM [dbo].[vw_CS_OrderDetails] AS [vw_CS_OrderDetails]
) AS [Extent1]
WHERE UPPER([Extent1].[RegNumber]) LIKE '%SD59BBO%'
ESCAPE N'~'
) AS [Project1]
) AS [Project1]
WHERE [Project1].[row_number] > 0
ORDER BY [Project1].[DateCreated] DESC
手摇版本更小,不到一秒钟即可完成。
鉴于第一个查询的效率低下,我有什么方法可以影响它创建的查询中的EF 6?
我可能不得不求助于存储过程,是否有任何好的模式可以将EF代码首先与存储过程集成在一起?
编辑 :根据来自Wahid Bitar的请求,这是我用来创建上述SQL的LINQ。
var query = _dbSet
.Where(o => o.RegNumber.ToUpper().Contains(searchTerm))
.OrderByDescending(c => c.DateCreated)
.Skip(skip)
.Take(pageSize);
实际上,它在一些辅助方法中有点散布,但主要是它。 最终在query
调用ToList()
,枚举结果集。
编辑 :根据要求的手摇SQL:
SELECT *
FROM
(
SELECT ROW_NUMBER() OVER ( ORDER BY DateCreated DESC ) AS RowNum
,[Branch]
,[Salesman]
,[Status]
,[OrderID]
,[DateCreated]
,[DateCompleted]
,[RegNumber]
,[Make]
,[Model]
,[Spec]
,[Title]
,[Firstname]
,[Surname]
,[Address1]
,[Address2]
,[Address3]
,[Town]
,[County]
,[Postcode]
,[HomePhone]
,[WorkPhone]
,[MobilePhone]
,[EMailAddress]
,[AllowMarketing]
,[Manager]
,[FK_BranchID]
FROM [SalesmanOffice2].[dbo].[vw_CS_OrderDetails]
WHERE RegNumber LIKE '%SD59BBO%'
) AS NumberedRows
WHERE NumberedRows.RowNum BETWEEN 1 AND 15 ORDER BY RowNum
我在您的EF查询中注意到您调用ToUpper
,但是您在手动查询中没有这样做。 如果RegNumber
是一个索引字段,则在进行比较之前将其传递给SQL的UPPER
函数将使其无法使用该索引。 这可能是性能较差的原因。
您可能在那里有ToUpper
,因为.Net的字符串比较区分大小写。 但是,在执行EF查询时,它只是将其转换为SQL比较。 因此,由于SQL不区分大小写(默认情况下),因此EF不区分大小写。
删除对ToUpper
调用,看看是否可以提高性能。
编辑
看起来EF也将SD57WBO
视为Unicode。 这也可能会抛出索引。
RegNumber
的数据类型是RegNumber
? 如果它是varchar
/ char
而不是nvarchar
/ nchar
,则可能需要指定该列不是unicode。 在EF Code First Fluent API中,您需要在属性上使用命令IsUnicode(false)
。
让我补充一点,顺序是不同的。 鉴于存在top子句,它可能会在top之前强制实现。 手动查询中没有排序依据。 我敢肯定,如果您通过删除命令来获得更简单的SQL ...
到目前为止,这基本上意味着您可以真正比较两个不同的查询。 抱歉,但是cadrell0和我指出语义差异后,问题还剩下什么? 一个比较慢-是的。 SQL很烂-是的。 但是,您会做不同的事情,这就是速度差异的来源。
正如@Jim Wooley强调的那样,我在尝试进行部分匹配的列上没有全文索引。 鉴于视图非常大,这意味着在不使用索引的情况下比较了每一行。 我尚未在启用了文本索引的视图上测试过这些查询,因此无法确定,但是我已经测试过,当搜索词与列完全匹配并且我使用的是相等运算符时,例如:
WHERE RegNumber = 'SD59BBO'
在所有其他条件相同的情况下,查询将在更可取的时间内执行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.