Count VS select в LINQ - что быстрее?

Я использую интерфейсы IQueryable<T> во всем моем приложении и откладываю выполнение SQL в базе данных до тех пор, пока такие методы, как .ToList()

Мне нужно будет иногда находить количество определенных списков - без необходимости использовать данные в подсчете списка. Я знаю из своего опыта SQL, что SQL COUNT() гораздо меньше работает для БД, чем эквивалентный оператор SELECT, который возвращает все строки.

Итак, мой вопрос: будет ли меньше работать с БД, чтобы вернуть счетчик из метода IQueryable<T> Count(), чем преобразовать IQueryable<T> в список и вызвать метод списка Count()?

Я подозреваю, что он даст, что ToList() запустит SELECT sql, а затем в отдельном запросе подсчет строк. Я надеюсь, что Count() на IQueryable<T> просто отображает sql для SQL-запроса sql(). Но я не уверен. Знаете ли вы?

Ответ 1

Вызов ToList() вернет подлинный List<T> со всеми данными, что означает выборку всех данных. Нехорошо.

Вызов Count() должен действительно заставить SQL делать счет на стороне базы данных. Гораздо лучше.

Простейший способ проверить это, однако, состоит в том, чтобы включить ведение журнала в контексте данных (или независимо от того, что эквивалентно для вашего конкретного поставщика) и посмотреть, какие запросы действительно отправляются.

Ответ 2

Я не уверен, что это жесткое и быстрое правило, но метод linq, который вы добавляете в Iqueryable, будет добавлен в дерево выражений linq - если только они не являются одним из методов, которые фактически заставляют дерево оцениваться (например, ToList и Single и т.д.). В случае с LinqToSql вы узнаете, не может ли он что-то преобразовать в инструкцию SQL, потому что вы получите исключение во время выполнения, заявив, что метод не поддерживается.

например,

var something = dbContext.SomeTable
  .Select(c => c.col1 == "foo")
  .Distinct()
  .ToList()
  .Count()

В вышеизложенном, Select() и Distinct() включены в SQL-запрос, переданный на сервер, потому что они добавлены в Iqueryable. Count() просто действует в списке, который был возвращен sql-запросом. Поэтому вы не хотите так поступать: -)

В вашем случае Count() определенно будет быстрее, чем Select(), потому что итоговый оператор sql действительно включит счет, поэтому серверу нужно вернуть только одно число, а не список строк.

Ответ 3

Если вы используете SQL Server, Count() по-прежнему очень дорог, потому что он вызывает сканирование таблицы (или сканирование индекса, см. комментарии к основному ответу). И, по умолчанию, Linq не использует прочитанный уровень незащищенной изоляции, что ухудшает ситуацию из-за блокировки.

Если вы можете жить с результатом, являющимся грязным результатом и приближением к общему количеству строк, следующий код будет значительно быстрее, чем использование Count(). По моему опыту, значение, возвращаемое этим кодом, редко отличается от истинного количества строк.

/// <summary>A very fast method for counting rows in a table.</summary>
public static long FastRowCount(DataContext context, string tableName)
{
    const string template = "SELECT rowcnt FROM sys.sysindexes WHERE id = OBJECT_ID('{0}') AND indid < 2";
    string query = string.Format(template, tableName);
    return context.ExecuteQuery<long>(query).Single();
}