Изучая различия между 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
может быть не таблицей, а представлением, и планы запросов станут в таком случае еще сложнее.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Есть приложение, к которому приходит запрос и приложение его обрабатываетВ процессе обработки могут вылезти болячки, которые надо залогировать...
У меня есть абстрактный класс Izdanie, от которого наследуются классы Kniga и JournalОт Kniga наследуется класс Uchebnik
ЗдравствуйтеПосле обработки данных на сервере данные выводятся в Repeater, который выглядит следующим образом:
Как можно при наведении мыши на кнопку перемещать ее (так же, только еще в обратную сторону нужно)? Этот код криво справляетсяТ