如何优化此查询?
// This will return data ranging from 1 to 500,000 records
List<string> products = GetProductsNames();
List<Product> actualProducts = (from p in db.Products
where products.Contains(p.Name)
select p).ToList();
如果我发送一个44000个字符串的列表,这个代码需要大约30秒来填充actualProducts
,不知道500000条记录需要什么。:(
有什么方法可以调整这个查询吗?
注意:每次调用(忽略第一个缓慢的edmx调用)几乎需要这么多时间
对500000条记录的IN
查询总是一种病理情况。
首先,确保数据库中Name
上有一个索引(可能不是聚集的)。
想法(都涉及到ADO.NET):
- 使用"表值参数"传入值,并将
INNER JOIN
传递给TSQL中的表值参数 - 或者创建具有列
QueryId
(可以是uniqueidentifier
)和Name
的形式为ProductQuery
的表;发明一个guid来表示您的查询(Guid.NewGuid()
),然后使用SqlBulkCopy
将500000对(每行上的guid相同;不同的guid是不同的查询)快速推送到表中;然后使用TSQL在两个表之间执行INNER JOIN
事实上,这些都很相似,但第一个可能是第一个尝试的东西。设置较少。
如果您不想使用数据库,可以尝试使用Dictionary<string,string>
如果没有错的话,我怀疑products.Contains(p.Name)
是昂贵的,因为它是O(n)运算。尝试将GetProductsNames
返回类型更改为Dictionary<string,string>
或将List转换为Dictionary
Dictionary<string, string> productsDict = products.ToDictionary(x => x);
所以您手头有一本字典,现在将查询重写为以下
List<Product> actualProducts = (from p in db.Products
where productsDict.ContainsKey(p.Name)
select p).ToList();
这将帮助您大大提高性能(缺点是分配双倍内存,优点是性能)。我用非常大的样本进行了测试,结果很好。试试看。
希望这能有所帮助。
您还可以使用散列方法,使用name列作为传递给散列函数的值;然后,您可以迭代500K集合,对每个名称进行哈希函数处理,并测试本地哈希文件中是否存在。这将需要比linq方法更多的代码,但它可能比对后端执行内部联接的重复调用快得多。