Мне любопытно о том, как точно LINQ (не LINQ к SQL) работает, соединения негласно относительно того, как SQL-сервер выполняет соединения.
SQL-сервер прежде, чем выполнить запрос, генерирует План выполнения. Планом выполнения является в основном Дерево выражений на том, чему он верит, лучший способ выполнить запрос. Каждый узел предоставляет информацию о том, сделать ли Вид, Сканирование, Выбор, Соединение, ect.
На узле 'Соединения' в нашем плане выполнения мы видим три возможных алгоритма; Хэширование, Слияние и Соединение Вложенных циклов. SQL-сервер выберет, какой алгоритм к для каждой операции Соединения на основе ожидаемого количества строк во Внутренних и Внешних таблицах, какое соединение мы делаем (некоторые алгоритмы не поддерживают все типы соединений), нужны ли нам данные, заказанные, и вероятно много других факторов.
Алгоритмы соединения:
Соединение Вложенного цикла: Лучше всего для маленьких исходных данных, может быть оптимизирован с заказанной внутренней таблицей.
Слияние: Лучше всего для отсортированных исходных данных средних и крупных исходных данных или вывода, который должен быть заказан.
Хэширование: Лучше всего для средних и крупных исходных данных, может быть параллелизирован для масштабирования линейно.
Запрос 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-сервер мог бы использовать Соединение Вложенного цикла, если он знает, что существует небольшое количество строк от каждой таблицы, слияние, если он знает, одна из таблиц имеет индекс, и Хэширование, если он знает, существует много строк на любой таблице, и ни у одного нет индекса.
Linq выбирает свой алгоритм для соединений? или это всегда использует тот?
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
в первую очередь может потребоваться полная итерация, поэтому в большинстве случаев быстрее просто предположить худшее и использовать алгоритм хеширования.
Сам LINQ не выбирает алгоритмы какого-либо вида, поскольку LINQ, строго говоря, это просто способ выражения запроса в SQL-подобном синтаксисе, который может отображаться на вызовы функций либо IEnumerable
, либо IQueryable
. LINQ является полностью особенностью языка и не предоставляет функциональности, а только другой способ выражения существующих вызовов функций.
В случае IQueryable
выбор наилучшего метода получения результатов полностью зависит от провайдера (например, LINQ to SQL).
В случае LINQ to Objects (использующего IEnumerable
) во всех случаях используется простое перечисление (примерно эквивалентное вложенным циклам). Нет глубокой проверки (или даже знания) базовых типов данных для оптимизации запроса.
Методы в System.Linq.Enumerable
выполняются в порядке их выдачи. Оптимизатора запросов нет.
Многие методы очень ленивы, что позволяет не полностью перечислить источник, помещая .First
или .Any
или . Возьмите
в конце запрос. Это самая простая оптимизация.
В частности, для System.Linq.Enumerable.Join в документации указано, что это хеш-соединение.
Компаратор проверки на равенство по умолчанию, 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