#c_sharp #entity_framework #linq
Изучая различия между 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) Хотелось бы узнать какой подход в данном случае будем эффективней и почему?
Ответы
Ответ 1
Смотрите. Базы данных предназначены, кроме всего прочего, и для того, чтобы быстро и эффективно работать с большими объёмами данных. Например, базы данных легко справляются с выборкой из таблиц, больших по размеру, чем оперативная память. А вот получение целой такой таблицы в память с целью последующей фильтрации уже провалится по перерасходу памяти. Также и фильтрация в базе данных может быть очень быстрой, т. к. если у неё есть индекс на фильтруемое поле, она может совместить фильтрацию с выборкой. (А особо умные базы данных могут, исходя из запросов, такой индекс и выстроить сами.) Если индекса нет, то всё равно польза от фильтрации на стороне базы в том, что выброшенные фильтром данные не нужно перегонять из базы в программу, и не нужно создавать их в основной программе только для того, чтобы тут же выбросить. Поэтому обычно имеет смысл те части запроса, которые можно выполнить в базе данных (это та часть, при которой вы остаётесь в рамках IQueryable), выполнять на ней. (Я не уверен, относится ли эта рекомендация к вложенным подзапросам, пускай лучше специалисты по базам данных поправят меня.) К сожалению, скорость операций на базе данных имеет и обратную сторону: не все операции можно выполнить на базе данных, поэтому промежуточные результаты запроса приходится материализовать (то есть, получать из базы и загружать в память), используя ToList или AsEnumerable, и «дорабатывать напильником» в самой программе. «Умения» различных баз данных отличаются между собой. Обычно база данных должна уметь фильтровать не только по равенству поля значению, но и уметь сравнивать с константой. Судя по всему, либо вам попался код для менее продвинутой базы данных (да, абстракции протекают), либо автор кода ошибся. Либо это «костыль» под какой-нибудь баг. Если сравнение возможно выполнить на базе данных, его стоит именно там и выполнять.Ответ 2
В общем случае, вы конечно правы, проще получить одним запросом к БД, чем фильтровать потом в оперативной памяти. Но тут есть несколько нюансов. Во-первых мы должны учитывать, что в базе данных таблица несколько более сложна чем мы можем видеть в нашем коде, с ней связаны еще такие сущности как и индексы, триггеры и тд... Поэтому добавление какого либо дополнительного условия, может порождать "плохой" план запроса с полным перебором довольно больших объемов данных (например если Id входит только в составной индекс, да, для поля с таким названием это было бы странно, но... ). В таком случае, иногда, проще выбрать только небольшую часть данных, материализовать их (ToList()), и отфильтровать их уже в памяти, вместо проведения рефакторинга всей БД. Во-вторых Phones может быть не таблицей, а представлением, и планы запросов станут в таком случае еще сложнее.
Комментариев нет:
Отправить комментарий