我需要优化以下EF查询,因为最初我将从CustomerWidgets表中恢复所有内容。现在该表有100,000行,它开始变慢。我正在查询sql server dbase。
我想我不需要先把所有东西都带回去,那么修改下面代码的最佳方法是什么?
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;
}
答案 0 :(得分:4)
仅在结尾处致电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()
次调用直到最后,您使用IQueryable
,只是添加Where
子句,只有在您调用{{1}时才构造和检索所以你的SQL查询最终会像:
ToList()
哪个应该只检索您要查询的确切项目
对于额外的优化,您可能希望删除所有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
并在SQL服务器中设置不区分大小写的排序规则(名称中包含ToUpper
的排序规则):具有正确的索引,这应该 MUCH 比在查询中将字符串转换为大写更快。
此外,您的CI
替换(适用于ZipCode
至" "
)最好退出""
子句,以便您可以这样做:
Where
这仍然会在列tho上调用 if (!string.IsNullOrEmpty(ZipCode))
{
ZipCode = ZipCode.Replace(" ", "");
widgetSearchResults =
widgetSearchResults.Where(
x => x.ZipCode.Replace(" ", "").ToUpper().Contains(ZipCode.ToUpper()));
}
SQL函数(不在REPLACE
子句上)。你不能在那里替换任何东西:SQL Server检查额外的空白空间比为每一行调用一个函数要快。但是警告:这可能会改变功能:使用当前代码,“01 05”将匹配“0 105”;没有替换,“01 05”将仅匹配“01 05”。如果这对您不起作用,请保留替换。
PS:我没有测试你的查询是否会直接转换为SQL Server(如果不这样做可能会引发异常)......乍一看,我认为它们应该转换得很好,但如果他们没有,首先应该很容易弄清楚这些表达。
除此之外,如果您的应用程序允许,您可以使用分页(在Linq中使用where
和Skip
),如果您想一次检索更少的元素...但我没有看到你可以采取的优化措施。
回顾一下:
Take
子句构造查询,而不是检索所有内容,然后在内存中过滤。这是通过在末尾调用WHERE
来完成的。ToList()
)。如果您可以更改列排序规则,SQL Server将执行不区分大小写的搜索。ToUpper
替换" "
。如果你不这样做会更快。检查答案文本上的警告。