2014-02-12 71 views
1

我一直在使用實體框架6代碼首先在我的域模型上執行一些簡單的CRUD操作,目前它的表現令人讚歎。替換由實體框架生成的慢SQL的選項6

我現在遇到了一個情況,我正在執行一個相當複雜的查詢,它涉及過濾和分頁結果。由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 
+1

請問您可以添加您編寫的c#linq代碼以獲取此Sql查詢。 –

+0

你的手動查詢是什麼樣的? – cadrell0

+0

根據你的命名約定,看起來你是在視圖上運行它。我發現EF會添加所有列,即使它們不需要或映射到視圖的模型中。您可以通過直接點擊底層表來優化它。另外,這個視圖很可能沒有被索引,這是直接訪問表可能會改進性能的地方。在這樣一個簡單的查詢中,缺少索引是我看到EF在性能方面遙遙領先的唯一途徑。 –

回答

0

作爲@Jim Wooley強調,我沒有對我試圖做部分匹配的列上的全文索引。鑑於視圖非常大,這意味着每行都在沒有索引的情況下進行比較。我還沒有測試這些查詢與啓用,因此文本索引視圖不能肯定地說,但我已經測試,當搜索項列完全匹配,我使用相等運算符,如:

WHERE RegNumber = 'SD59BBO' 

在所有其他條件相同的情況下,查詢會在更理想的時間執行。

1

我在你的EF查詢發現你打電話ToUpper,但是你是不是在你的操作手冊中查詢這樣做。如果RegNumber是一個索引字段,在進行比較之前將它傳遞給SQL的UPPER函數將使其無法使用該索引。這可能是性能更差的原因。

您可能在那裏有ToUpper,因爲.Net的字符串比較區分大小寫。但是,執行EF查詢時,它只是將其轉換爲SQL比較。因此,由於SQL不區分大小寫(默認情況下),EF不區分大小寫。

取出撥打電話ToUpper,看看是否可以提高性能。

編輯

它也像EF被處理SD57WBO爲Unicode。這也可能導致你的索引。

什麼是RegNumber的數據類型?如果是varchar/char而不是nvarchar/nchar,則可能需要指定該列不是unicode。在EF Code First,Fluent API中,您需要在您的財產上使用命令IsUnicode(false)

+0

同意,而且很好。不幸的是,這並沒有造成巨大的差異。我已經從EF查詢中刪除了UPPER,並且它可能快2秒,並且UPPER也添加到了手動查詢中,並且速度稍慢了一秒。仍然有很大的差異,並認爲它可能是別的。 –

+0

是什麼讓你提到Unicode的問題? RegNumber是一個C#字符串,在數據庫中它是一個varchar(10) –

+0

'ESCAPE N'〜''看起來像是在處理Unicode。 – cadrell0

1

讓我補充說明順序是不同的。考慮到有一個頂級的條款,它可能會在頂層之前強制實現。在手動查詢中沒有排序。我相信如果你通過更簡單的SQL刪除命令...

現在基本上意味着你真的比較了2個不同的查詢。對不起,現在的問題還剩下那個問題,我和Cadrell指出了語義差異?一個更慢 - 是的。 SQL是垃圾 - 是的。但是,嘿,你做了不同的事情,這是速度差異的來源。

+0

在手動查詢中的順序是OVER(ORDER BY DateCreated)我以前省略了DESC。 –

+0

然後你被卡住了。 YOu碰到了一個不太智能的ql世代EF的限制 - 這是它遺憾的重要問題之一。超語義學不是他們支持的東西 - 有時候EF最好打到SP或者特殊視圖,因爲有些東西不能很好地表達出來。 – TomTom

+0

我已經更新了EF到最新版本,唉,我已經走了一條不同的路線,因爲我被允許在這些表格上進行全文索引的概率非常渺茫。感謝您的幫助! –