You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

LINQ IQueryable性能对比:关联对象查询VS直接筛选子表查询

关于两种LINQ查询的性能对比与等价性分析

咱先把两个问题拆开来聊:首先是两个查询是否等价,然后再看谁的性能更优

一、等价性判断

这俩查询是否等价,完全取决于你第一个查询里的Candidates集合范围:

  • 如果Candidates集合恰好是所有candIds对应的候选人(比如你先通过Candidates.Where(c => candIds.Contains(c.Id))筛选过),那两个查询的最终结果是等价的——都是拿到这些候选人的符合条件的通知记录。
  • 但如果Candidates是未经过筛选的全表数据,或者包含了不在candIds里的候选人,那结果就完全不一样了:第一个查询会拿到所有候选人(包括不在目标ID里的)的符合条件的通知,而第二个只拿目标ID对应的通知。

简单说:只有当Candidates集合的范围和candIds完全匹配时,二者才等价,否则不等价。

二、性能对比:第二种查询完胜

不管是LINQ to EF这类数据库查询场景,还是LINQ to Objects的内存集合操作,第二种查询的性能都要显著优于第一种,原因如下:

1. 数据库查询场景(LINQ to EF)

  • 第一种查询Candidates.SelectMany(c=>c.Notifications.Where(...).ToList()),EF大概率会生成N+1查询:先查所有符合条件的Candidates(1次查询),然后对每个候选人单独查询其Notifications(N次查询,N是候选人数量)。就算EF优化成了JOIN查询,也是基于两张表的关联来检索,当候选人数量多的时候,JOIN的成本会很高。
  • 第二种查询Notifications.Where(n=>candIds.Contains(n.CandidateId) && ...).ToList(),是直接对Notifications表发起查询,用CandidateId IN (candIds)加上其他过滤条件。如果Notifications表的CandidateId字段建了索引,数据库可以直接通过索引快速定位到目标记录,只需要1次查询,效率高得多。

2. 内存集合场景(LINQ to Objects)

  • 第一种需要先遍历所有Candidates,再对每个候选人的Notifications集合做筛选,相当于两层嵌套遍历,当数据量较大时,时间复杂度更高。
  • 第二种直接遍历Notifications集合一次,通过candIds.Contains做过滤,虽然Contains是O(k)(k是candIds的长度),但整体来说比两层遍历的成本低,尤其是当Notifications的数量远大于Candidates的时候。

总结

  • 等价性:仅当Candidates集合与candIds的范围完全匹配时,二者等价;否则不等价。
  • 性能:第二种查询的性能明显更优,无论是数据库查询还是内存集合操作场景。

内容的提问来源于stack exchange,提问作者Rdq

火山引擎 最新活动