性能:.Join与.Contains - Linq to Entities

13

我正在使用Linq to Entities查询数据库以获取整数列表以进行进一步处理。我有两种获取列表的方法,如下:

第一种方法是:

List<int> lstBizIds = new List<int>() { 1, 2, 3, 4, 5 };
List<int> lstProjectIds = context.Projects.Where(x => lstBizIds.Contains(x.businessId)).Select(x => x.projectId).ToList();

其次是:

List<int> lstBizIds = new List<int>() { 1, 2, 3, 4, 5 };
List<int> lstProjectIds = context.Projects.Join(lstBizIds, p => p.businessId, u => u, (p, u) => p.projectId).ToList();

现在我的问题是,上述方法中哪一种性能更好?如果第一个列表lstBizIds增长,这是否会影响性能?如果有其他性能更优的实现方式,请给出建议。


1
你确定第一个选项可行吗?查询是否执行并给出结果? - Sefe
是的,肯定会起作用。 - Girish Vadhel
1
抱歉,我是指第二个选项。无论如何,我假设你已经成功尝试了两个选项。 - Sefe
不涉及性能问题,但在选择两者之间时需要注意的另一个问题是,在EF Core中,当进行Where INJOIN操作时,不同的连接器/提供程序会以不同的方式转义字符。这在我使用Pomelo MySQL for EF Core时曾经困扰过我,但我认为任何提供程序都可能存在此问题。 - Collin Barrett
4个回答

6

你应该选择Contains,因为EF可以生成更高效的查询。

这将是SQL联接:

SELECT Id
FROM Projects
INNER JOIN (VALUES (1), (2), (3), (4), (5)) AS Data(Item) ON Projects.UserId = Data.Item

这将是 SQL 中的包含查询:

SELECT Id
FROM Projects
WHERE UserId IN (1, 2, 3, 4, 5, 6)

INJOIN 更高效,因为DBMS可以在第一次匹配IN后停止查找; 而JOIN则始终会继续运行,即使有了第一个匹配。

您可能还想检查实际发送到DB的查询。您必须始终比较SQL而不是LINQ代码(显然)。


6
对于生成的SQL,你是正确的(虽然SqlServer的join看起来略有不同)+1。但是对于哪个更有效率,这取决于数据库查询计划优化器。大多数现代查询优化器将产生相同的执行计划。 - Ivan Stoev
9
请注意,对于相对较大的数据集(例如> 1000个项目),在Entity Framework中使用Contains操作非常缓慢。这是由于内部原因导致的缓慢,而不是生成的查询本身缓慢。因此,最好永远不要在大数据集上使用Contains操作。 - Evk
相反,最近我进行了一项测试,比较了Contains和Join,结果发现Contains在所有方面都比Join更好。在查询执行方面,Contains比Join更快。而且在SQL生成方面也更快。使用100K个项目进行Join会在SQL生成过程中导致StackOverflow异常。 - Xiaoguo Ge
1
我决定使用join语法,因为它在代码中看起来更整洁,但我刚刚发现这是一个坏主意!结果的SQL非常庞大(它为每个元素创建一个联合),并且当它比较的集合非常大时,实体框架在运行时完全失败。花了我很长时间才找到一个正在运行的网站失败的原因。用Contains替换Joins,现在一切都好了。 - userSteve

2
执行联接操作非常高效,因为Where条件实际上执行了所有表的笛卡尔积,然后过滤满足条件的行。这意味着Where条件对每个行组合进行评估(n1 * n2 * n3 * n4)。
Join操作符从第一个表中获取行,然后仅从第二个表中获取具有匹配键的行,然后仅从第三个表中获取具有匹配键的行,以此类推。其次,包含操作将以迭代方式工作,使其比联接慢。

是的,我也在 stack overflow 的一个链接中发现了这个问题:https://dev59.com/z2035IYBdhLWcg3wNtRw 但是在这里我也找到了相反的解释:http://stackoverflow.com/questions/32650224/innerjoin-vs-contains-which-is-faster - Girish Vadhel
2
这适用于LINQ to Objects,但问题是关于LINQ to Entities的。 - Ivan Stoev
Join方法在内部使用索引来连接两个结果集,但Where方法使用一种组合机制,使其速度较慢。而Join方法则更快,因为它知道如何组合以减少结果到相关组合。当您使用Where方法指定关系时,它必须创建每个可能的组合,这将导致速度变慢。希望这回答了您的问题。 - g.005
您可能忽略了其中一方是数据库表,而另一方是内存列表的部分。 - Ivan Stoev

1

我刚花了相当长的时间来查找一个程序中引起堆栈溢出错误的原因,该程序包含几个简单的LINQ查询,访问一个中等大小的数据库。

对于一侧有大约10k个元素的ICollection和另一侧的SQL表,从“join”到“Contains”的单个更改修复了堆栈溢出错误。

似乎尽管性能比较,但“Contains”是一个更安全的选择。


0


我选择第一个,因为它不会增加计算机的内存。
如果你要使用两个数组来比较条件,请选择第二个。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接