簡體   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