我一直在使用秒表,看起来下面的查询在性能方面非常昂贵,即使我已经在下面找到了基于各种读数的最佳选择(使用for更改foreach循环,使用数组)而不是集合,使用匿名类型不从DB获取整个表)。有没有办法让它更快?我需要填写价格数组,这需要可以为空。我不确定我是否遗漏了某些东西?
public float?[] getPricesOfGivenProducts(string[] lookupProducts)
{
var idsAndPrices = from r in myReadings select
new { ProductId = r.ProductId, Price = r.Price };
float?[] prices = new float?[lookupProducts.Length];
for(int i=0;i<lookupProducts.Length;i++)
{
string id = lookupProducts[i];
if (idsAndPrices.Any(r => r.ProductId == id))
{
prices[i] = idsAndPrices.Where(p => p.ProductId == id)
.Select(a=>a.Price).FirstOrDefault();
}
else
{
prices[i] = null;
}
}
return prices;
}
答案 0 :(得分:4)
每次拨打idsAndPrices.Any(r => r.ProductId == id)
时,您都可能正在访问数据库,因为您还没有实现结果(.ToList()
会稍微修复一下)。这可能是表现不佳的主要原因。但是,只需将其全部加载到内存中仍然意味着您每次都会在列表中搜索productID(实际上每个产品两次)。
在您尝试进行查找时使用Dictionary
。
public float?[] getPricesOfGivenProducts(string[] lookupProducts)
{
var idsAndPrices = myReadings.ToDictionary(r => r.ProductId, r => r.Price);
float?[] prices = new float?[lookupProducts.Length];
for (int i = 0; i < lookupProducts.Length; i++)
{
string id = lookupProducts[i];
if (idsAndPrices.ContainsKey(id))
{
prices[i] = idsAndPrices[id];
}
else
{
prices[i] = null;
}
}
return prices;
}
为了进一步改进,我们可以确定我们仅关心在阵列中传递给我们的产品。所以我们不要加载整个数据库:
var idsAndPrices = myReadings
.Where(r => lookupProducts.Contains(r.ProductId))
.ToDictionary(r => r.ProductId, r => r.Price);
现在,如果我们无法找到产品,我们可能希望避免返回零价格。场景。也许产品ID的有效性应该在别处处理。在这种情况下,我们可以使方法更简单(我们也不必依赖于按顺序排列数组):
public Dictionary<string, float> getPricesOfGivenProducts(string[] lookupProducts)
{
return myReadings
.Where(r => lookupProducts.Contains(r.ProductId))
.ToDictionary(r => r.ProductId, r => r.Price);
}
与绩效无关的说明,您应该use decimal
for money
答案 1 :(得分:2)
假设idsAndPrices
是IEnumerable<T>
,您应该进行初始化:
var idsAndPrices = (from r in myReadings select
new { ProductId = r.ProductId, Price = r.Price })
.ToList();
可能会拨打:
idsAndPrices.Any(r => r.ProductId == id)
和
idsAndPrices.Where(p => p.ProductId == id)
..导致IEnumerable<T>
每次被调用时都会被评估。
答案 2 :(得分:2)
基于
使用匿名类型不从DB中获取整个表
我认为myReadings
是数据库表和
var idsAndPrices =
from r in myReadings
select new { ProductId = r.ProductId, Price = r.Price };
是数据库查询。
你的实现远非最优(我宁愿说非常低效),因为上述查询每{/ 1}}个数组{/ 1}}和{{}每个元素执行两次1}}陈述。
我看到的最佳方式是尽可能多地过滤数据库查询,然后使用最有效的LINQ to Objects方法关联内存序列中的两个 - lookupProducts
,在您的情况下left outer join
:
idsAndPrices.Any(...)
答案 3 :(得分:0)
Any
和FirstOrDefault
是O(n)并且是多余的。只需删除All
电话,您就可以加快50%的速度。 FirstOrDefault
会返回null,因此请使用它来获取产品对象(删除Select
)。如果你想真正加快速度,你应该在设置prices[p.ProductId] != null
之前浏览产品并检查是否prices[p.ProductId] = p.Price
。
答案 4 :(得分:0)
var idsAndPrices = (from r in myReadings select
new { ProductId = r.ProductId, Price = r.Price })
.ToList();
for(int i=0;i<lookupProducts.Length;i++)
{
string id = lookupProducts[i];
prices[i] = idsAndPrices.FirstOrDefault(p => p.ProductId == id);
}
更好
Dictionary<Int, Float?> dp = new Dictionary<Int, Float?>();
foreach(var reading in myReadings)
dp.add(r.ProductId, r.Price);
for(int i=0;i<lookupProducts.Length;i++)
{
string id = lookupProducts[i];
if(dp.Contains(id)
prices[i] = dp[id];
else
prices[i] = null;
}