按主键选择多个实体的最有效方法是什么?
public IEnumerable<Models.Image> GetImagesById(IEnumerable<int> ids)
{
//return ids.Select(id => Images.Find(id)); //is this cool?
return Images.Where( im => ids.Contains(im.Id)); //is this better, worse or the same?
//is there a (better) third way?
}
我意识到我可以做一些性能测试来进行比较,但我想知道是否有一种比这两种方法更好的方法,并且正在寻找这两种查询之间的差异的一些启示,如果有的话,一旦它们被“翻译”。
4条答案
按热度按时间rhfm7lfc1#
在Entity Framework中使用
Contains
实际上是非常慢的。确实,它转换成SQL中的IN
子句,并且SQL查询本身执行得很快。但是问题和性能瓶颈是在从LINQ查询到SQL的转换中。将创建的表达式树扩展为OR
串联的长链,因为没有表示IN
。当创建SQL时,这个包含许多OR
的表达式被识别并折叠回SQLIN
子句。这并不意味着使用
Contains
比对ids
集合中的每个元素发出一个查询更糟糕(你的第一个选择)。它可能更好-至少对于不太大的集合。但是对于大的集合,它真的很糟糕。我记得我以前测试过一个Contains
查询,大约12。000个元素,虽然SQL中的查询在不到一秒的时间内执行,但它仍然工作了大约一分钟。可能值得测试多次往返数据库的组合的性能,每次往返的
Contains
表达式中的元素数较少。此方法以及将
Contains
与Entity Framework一起使用的限制将在此处显示和解释:为什么Contains()操作符会如此显著地降低Entity Framework的性能?
在这种情况下,原始SQL命令可能会执行得最好,这意味着您调用
dbContext.Database.SqlQuery<Image>(sqlString)
或dbContext.Images.SqlQuery(sqlString)
,其中sqlString
是@Rune的答案中显示的SQL。以下是一些测量值:
我已经在一个有550000条记录和11列(ID从1开始,没有间隔)的表上完成了这个操作,并随机挑选了20000个ID:
结果-〉毫秒= 85.5秒
结果-〉毫秒= 84.5秒
AsNoTracking
的这种微小影响非常不寻常,它表明瓶颈不是对象实体化(也不是SQL,如下所示)。对于这两个测试,可以在SQL事件探查器中看到SQL查询到达数据库的时间非常晚。(我没有精确测量,但晚了70秒。)显然,将此LINQ查询转换为SQL的成本非常高。
结果-〉毫秒= 5.1秒
结果-〉毫秒= 3.8秒
这一次禁用跟踪的效果更加明显。
结果-〉毫秒= 3.7秒
我的理解是
context.Database.SqlQuery<MyEntity>(sql)
与context.Set<MyEntity>().SqlQuery(sql).AsNoTracking()
相同,因此测试4和测试5之间预期没有差异。(The由于随机ID选择后可能重复,结果集的长度并不总是相同的,但它总是在19600和19640个元素之间。
即使到数据库的20000次往返也比使用
Contains
快:结果-〉毫秒= 73.6秒
请注意,我使用的是
SingleOrDefault
而不是Find
。(几分钟后我取消了测试)因为Find
在内部调用DetectChanges
。(context.Configuration.AutoDetectChangesEnabled = false
)的性能与SingleOrDefault
大致相同。使用AsNoTracking
可减少一到两秒的时间。测试是在同一台机器上的数据库客户端(控制台应用程序)和数据库服务器上进行的。由于多次往返,最后的结果可能会在"远程"数据库上变得更糟。
dgtucam12#
第二个选项肯定比第一个选项好。第一个选项将导致对数据库的
ids.Length
查询,而第二个选项可以在SQL查询中使用'IN'
操作符。它基本上会将您的LINQ查询转换为类似以下SQL的内容:其中value1,value2等是你的id变量的值。但是要注意,我认为用这种方法可以序列化到一个查询中的值的数量可能有一个上限。我会看看我是否能找到一些文档...
kcrjzv8t3#
嗯,最近我遇到了一个类似的问题,我发现最好的方法是在临时表中插入列表,然后进行连接。
这不是一个漂亮的方法,但对于大型列表,它是非常有性能的。
3pmvbmvn4#
使用toArray()将List转换为Array可提高性能。可以通过以下方式执行此操作: