[英]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
)...但是我看不到可以采取的進一步優化措施。
回顧一下:
WHERE
子句構造查詢,而不是檢索所有子句,然后過濾內存中的內容。 這是通過在最后調用ToList()
來完成的 ToUpper
)。 如果可以更改列排序規則,則SQL Server將執行不區分大小寫的搜索。 " "
與""
上的記錄和查詢子句兩者。 如果不這樣做,它將更快。 但是請檢查答案文本上的警告。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.