我很好奇LINQ(而不是LINQ to SQL)的表现是如何在Sql Server执行连接的情况下加入幕后的。
在执行查询之前,Sql Server生成执行计划。执行计划基本上是一个表达式树,它认为它是执行查询的最佳方式。每个节点都提供有关是否执行排序,扫描,选择,加入等信息。
在我们的执行计划中的“加入”节点上,我们可以看到三种可能的算法;散列连接,合并连接和嵌套循环连接。 Sql Server将根据Inner表和外表中的预期行数,我们正在做什么类型的连接(某些算法不支持所有类型的连接),是否需要数据排序,以及为每个Join操作选择哪种算法。可能还有很多其他因素。
加入算法:
嵌套循环加入: 最适合小输入,可以通过有序的内表进行优化。
合并加入: 最适合中到大输入分类输入,或需要订购的输出。
哈希加入: 最适合中到大输入,可以并行化,以线性扩展。
LINQ查询:
DataTable firstTable, secondTable;
...
var rows = from firstRow in firstTable.AsEnumerable ()
join secondRow in secondTable.AsEnumerable ()
on firstRow.Field<object> (randomObject.Property)
equals secondRow.Field<object> (randomObject.Property)
select new {firstRow, secondRow};
SQL查询:
SELECT *
FROM firstTable fT
INNER JOIN secondTable sT ON fT.Property = sT.Property
如果Sql Server知道每个表中有少量行,则可以使用嵌套循环连接,如果知道其中一个表有索引,则使用合并连接;如果知道有很多表,则使用散列连接两个表上的行都没有索引。
Linq是否选择其连接算法?还是总是使用一个?
答案 0 :(得分:6)
System.Linq.Enumerable
上的方法按发布顺序执行。没有查询优化器在起作用。
许多方法非常懒惰,这使您无法通过在查询末尾放置.First
或.Any
或.Take
来完全枚举来源。这是最简单的优化。
对于System.Linq.Enumerable.Join具体而言,the docs表明这是一个散列连接。
默认的相等比较器Default用于散列和比较密钥。
所以例子:
//hash join (n+m) Enumerable.Join
from a in theAs
join b in theBs on a.prop equals b.prop
//nestedloop join (n*m) Enumerable.SelectMany
from a in theAs
from b in theBs
where a.prop == b.prop
答案 1 :(得分:3)
Linq to SQL不会向服务器发送连接提示。因此,使用Linq to SQL的连接的性能将与“直接”发送到服务器的相同连接的性能相同(即使用纯ADO或SQL Server Management Studio),而不指定任何提示。
Linq to SQL也不允许你使用连接提示(据我所知)。因此,如果要强制使用特定类型的连接,则必须使用存储过程或Execute[Command|Query]
方法执行此操作。但除非您通过编写INNER [HASH|LOOP|MERGE] JOIN
指定连接类型,否则SQL Server始终会选择它认为最有效的连接类型 - 查询来自哪里无关紧要。
其他Linq查询提供程序(如Entity Framework和NHibernate Linq)将完成与Linq to SQL完全相同的操作。这些都没有直接了解您如何索引数据库,因此没有人发送连接提示。
Linq to Objects有点不同 - 它(几乎?)总是在SQL Server用语中执行“散列连接”。这是因为它缺少进行合并连接所需的索引,并且散列连接通常比嵌套循环更有效,除非元素的数量非常小。但是确定IEnumerable<T>
中元素的数量可能需要首先进行完整迭代,因此在大多数情况下,假设最差并使用散列算法会更快。
答案 2 :(得分:1)
LINQ本身并没有选择任何类型的算法,严格来说,LINQ只是一种表达类似SQL语法的查询的方式,可以映射到IEnumerable<T>
或{{1}上的函数调用}。 LINQ 完全是一种语言功能,不提供功能,只是表达现有函数调用的另一种方式。
在IQueryable<T>
的情况下,完全由提供者(例如LINQ to SQL)来选择产生结果的最佳方法。
对于LINQ to Objects(使用IQueryable<T>
),简单枚举就是在所有情况下使用的(大致相当于嵌套循环)。为了优化查询,没有对基础数据类型进行深入检查(甚至不了解)。