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();
}