繁体   English   中英

替换由Entity Framework 6生成的慢速SQL的选项

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM