Изучая различия между LINQ To Entites и LINQ To Objects в EntityFramework - столкнулся с интересной вещью :
var phones = db.Phones.Where(p=> p.CompanyId == 1).ToList().Where(p=> p.Id<10);
Здесь используются два метода Where, но их реализация будет различной. В первом случае, db.Phones.Where(p=> p.CompanyId == 1) транслируется в выражение SQL, которое было рассмотрено выше. Далее метод ToList() по результатам запроса создает список в памяти компьютера. После этого мы уже имеем дело со списком в памяти, а не с базой данных. И далее вызов Where(p=> p.Id<10) будет обращаться к списку в памяти и будет представлять Linq to Object.
И мне стало интересно : А не проще ли просто получать нужное значение из БД путем простого обращения через LINQ не используя при этом LINQ To Objects?
Ведь запрос в даном случае будет выглядеть так :
var phones = db.Phones
.Where(p => p.CompanyId==1 &&
p.TelephoneId<10)
Хотелось бы узнать какой подход в данном случае будем эффективней и почему?
Смотрите. Базы данных предназначены, кроме всего прочего, и для того, чтобы быстро и эффективно работать с большими объёмами данных. Например, базы данных легко справляются с выборкой из таблиц, больших по размеру, чем оперативная память. А вот получение целой такой таблицы в память с целью последующей фильтрации уже провалится по перерасходу памяти.
Также и фильтрация в базе данных может быть очень быстрой, т. к. если у неё есть индекс на фильтруемое поле, она может совместить фильтрацию с выборкой. (А особо умные базы данных могут, исходя из запросов, такой индекс и выстроить сами.) Если индекса нет, то всё равно польза от фильтрации на стороне базы в том, что выброшенные фильтром данные не нужно перегонять из базы в программу, и не нужно создавать их в основной программе только для того, чтобы тут же выбросить.
Поэтому обычно имеет смысл те части запроса, которые можно выполнить в базе данных (это та часть, при которой вы остаётесь в рамках IQueryable), выполнять на ней.
(Я не уверен, относится ли эта рекомендация к вложенным подзапросам, пускай лучше специалисты по базам данных поправят меня.)
К сожалению, скорость операций на базе данных имеет и обратную сторону: не все операции можно выполнить на базе данных, поэтому промежуточные результаты запроса приходится материализовать (то есть, получать из базы и загружать в память), используя ToList или AsEnumerable, и «дорабатывать напильником» в самой программе.
«Умения» различных баз данных отличаются между собой. Обычно база данных должна уметь фильтровать не только по равенству поля значению, но и уметь сравнивать с константой. Судя по всему, либо вам попался код для менее продвинутой базы данных (да, абстракции протекают), либо автор кода ошибся. Либо это «костыль» под какой-нибудь баг. Если сравнение возможно выполнить на базе данных, его стоит именно там и выполнять.
В общем случае, вы конечно правы, проще получить одним запросом к БД, чем фильтровать потом в оперативной памяти. Но тут есть несколько нюансов.
Во-первых мы должны учитывать, что в базе данных таблица несколько более сложна чем мы можем видеть в нашем коде, с ней связаны еще такие сущности как и индексы, триггеры и тд... Поэтому добавление какого либо дополнительного условия, может порождать "плохой" план запроса с полным перебором довольно больших объемов данных (например если Id входит только в составной индекс, да, для поля с таким названием это было бы странно, но... ). В таком случае, иногда, проще выбрать только небольшую часть данных, материализовать их (ToList()), и отфильтровать их уже в памяти, вместо проведения рефакторинга всей БД.
Во-вторых Phones может быть не таблицей, а представлением, и планы запросов станут в таком случае еще сложнее.
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости