簡體   English   中英

使用大記錄集優化實體框架查詢

[英]Optimise Entity Framework query with large record set

我需要優化以下EF查詢,因為最初我要從CustomerWidgets表中恢復所有內容。 現在表有100,000行,它開始變慢。 我正在查詢SQL Server數據庫。

我想我不需要先將所有內容都帶回來,那么修改以下代碼的最佳方法是什么?

        public List<CustomerWidget> SearchWidgets(string surname, string firstname, string ZipCode)
    {
        // i don't need to bring back everything here!!  optimize it I say!!
        var widgetSearchResults = _context.CustomerWidgets.Where(x => x.IsDeleted == false).ToList();

        if (!string.IsNullOrEmpty(surname))
        {
            widgetSearchResults =
                widgetSearchResults.Where(x => x.Surname.ToUpper().Contains(surname.ToUpper())).ToList();
        }


        if (!string.IsNullOrEmpty(firstname))
        {
            widgetSearchResults =
                widgetSearchResults.Where(x => x.Forename.ToUpper().Contains(firstname.ToUpper())).ToList();
        }

        if (!string.IsNullOrEmpty(ZipCode))
        {
            widgetSearchResults =
                widgetSearchResults.Where(
                    x => x.ZipCode.Replace(" ", "").ToUpper().Contains(ZipCode.Replace(" ", "").ToUpper()))
                    .ToList();
        }

        return widgetSearchResults;
    }

僅在最后調用ToList() ,並在之前使用IQueryable

public List<CustomerWidget> SearchWidgets(string surname, string firstname, string ZipCode)
{
    var widgetSearchResults = _context.CustomerWidgets.Where(x => x.IsDeleted == false);

    if (!string.IsNullOrEmpty(surname))
    {
        widgetSearchResults =
            widgetSearchResults.Where(x => x.Surname.ToUpper().Contains(surname.ToUpper()));
    }


    if (!string.IsNullOrEmpty(firstname))
    {
        widgetSearchResults =
            widgetSearchResults.Where(x => x.Forename.ToUpper().Contains(firstname.ToUpper()));
    }

    if (!string.IsNullOrEmpty(ZipCode))
    {
        widgetSearchResults =
            widgetSearchResults.Where(
                x => x.ZipCode.Replace(" ", "").ToUpper().Contains(ZipCode.Replace(" ", "").ToUpper()));
    }

    /********************
      Call ToList() only here
      *******************/

    return widgetSearchResults.ToList();
}

實際從數據庫中檢索元素時,EF會構造整個SQL查詢,因此,使用您的方法,您首先要檢索:

SELECT * FROM CustomerWidgets WHERE IsDeleted = false

然后,其余查詢從內存列表(共100.000個項目)中執行。

通過ToList()刪除ToList()調用,可以使用IQueryable ,它僅添加Where子句,並且僅在調用ToList()時進行構造和檢索,因此您的SQL查詢最終類似於:

SELECT * FROM CustomerWidgets WHERE 
           IsDeleted = false 
       AND UPPER(Surname) LIKE UPPER('%Something%')
       AND UPPER(FirstName) LIKE UPPER('%Something%')
       AND UPPER(ZipCode) LIKE UPPER('%Something%') -- plus all the replacing

哪個應該只檢索您要查詢的確切項目

對於額外的優化,你可能想要刪除所有這些ToUpper ,並設置SQL Server中的排序規則是不區分大小寫(那些有CI中的名稱):用正確的指標,這應該是遠遠快於轉換您的字符串中為大寫查詢。

另外,最好用ZipCode替換( " """ )退出Where子句,因此您可以執行以下操作:

    if (!string.IsNullOrEmpty(ZipCode))
    {
        ZipCode = ZipCode.Replace(" ", "");
        widgetSearchResults =
            widgetSearchResults.Where(
                x => x.ZipCode.Replace(" ", "").ToUpper().Contains(ZipCode.ToUpper()));
    }

這仍然會在tho列(而不是where子句)上調用REPLACE SQL函數。 您不能在那里替換任何內容:SQL Server檢查多余的空白空間要比為每一行調用一個函數更快。 但是有一個警告:這可能會改變功能:在您當前的代碼中,“ 01 05”將與“ 0 105”匹配; 如果不進行替換,則“ 01 05” 只能匹配“ 01 05”。 如果那對您不起作用,請保留替代品。

PS:我還沒有測試過您的查詢是否可以直接轉換為SQL Server(如果不將其轉換為異常,則可能會引發異常)...乍看之下,我認為它們應該轉換得很好,但是如果轉換不成功,它會轉換為SQL Server首先應該很容易計算出這些表達式。

除此之外,如果您的應用程序允許的話,如果您想一次檢索更少的元素,則可以使用分頁(在Linq中使用Skip and Take )...但是我看不到可以采取的進一步優化措施。

回顧一下:

  1. 使用WHERE子句構造查詢,而不是檢索所有子句,然后過濾內存中的內容。 這是通過在最后調用ToList()來完成的
  2. 刪除所有不必要的每行功能(在這種情況下,尤其是ToUpper )。 如果可以更改列排序規則,則SQL Server將執行不區分大小寫的搜索。
  3. 無需更換" """上的記錄和查詢子句兩者。 如果不這樣做,它將更快。 但是請檢查答案文本上的警告。
  4. 在SQL Server上進行正確的索引
  5. 如果這還不夠,請使用分頁並且您的應用程序支持分頁

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM