Какая разница между select_related и prefetch_related в Django ORM?
В Django Doc,
select_related()
"следует" за связями внешнего ключа, выбирая дополнительные данные связанного объекта при выполнении своего запроса.
prefetch_related()
выполняет отдельный поиск для каждого отношения и выполняет "соединение" в Python.
Что это значит под "объединением в python"? Может кто-нибудь проиллюстрировать примером?
select_related
я понимаю, для связи с внешним ключом используйте select_related
; и для отношения M2M используйте prefetch_related
. Это правильно?
Ответы
Ответ 1
Ваше понимание в основном верно. Вы используете select_related
когда объект, который вы собираетесь выбрать, является отдельным объектом, то есть OneToOneField
или ForeignKey
. Вы используете prefetch_related
когда собираетесь получить "набор" вещей, так что ManyToManyField
как вы заявили, или обратный просмотр ForeignKey
. Просто чтобы уточнить, что я подразумеваю под "обратным ForeignKey
s", вот пример:
class ModelA(models.Model):
pass
class ModelB(models.Model):
a = ForeignKey(ModelA)
ModelB.objects.select_related('a').all() # Forward ForeignKey relationship
ModelA.objects.prefetch_related('modelb_set').all() # Reverse ForeignKey relationship
Разница в том, что select_related
выполняет SQL-соединение и поэтому возвращает результаты как часть таблицы с SQL-сервера. prefetch_related
с другой стороны, выполняет другой запрос и, следовательно, уменьшает избыточные столбцы в исходном объекте (ModelA
в приведенном выше примере). Вы можете использовать prefetch_related
для всего, для чего вы можете использовать select_related
.
Компромисс заключается в том, что prefetch_related
должен создать и отправить список идентификаторов для выбора обратно на сервер, это может занять некоторое время. Я не уверен, есть ли хороший способ сделать это в транзакции, но я понимаю, что Django всегда просто отправляет список и говорит SELECT... WHERE pk IN (...,...,...) в принципе. В этом случае, если предварительно выбранные данные редки (скажем, объекты государства США, связанные с адресами людей), это может быть очень хорошо, однако, если они ближе к однозначному, это может привести к потере большого количества сообщений. Если есть сомнения, попробуйте оба варианта и посмотрите, какие из них лучше.
Все, что обсуждалось выше, в основном касается связи с базой данных. Однако на стороне Python prefetch_related
имеет дополнительное преимущество, prefetch_related
том, что один объект используется для представления каждого объекта в базе данных. С помощью select_related
дубликаты объектов будут создаваться в Python для каждого "родительского" объекта. Так как объекты в Python имеют приличную долю памяти, это также может быть рассмотрено.
Ответ 2
Оба метода достигают той же цели, чтобы отказаться от ненужных запросов db. Но они используют разные подходы к эффективности.
Единственная причина использования любого из этих методов - когда один большой запрос предпочтительнее многих небольших запросов. Django использует большой запрос для предварительного создания моделей в памяти, а не для выполнения запросов по требованию к базе данных.
select_related
выполняет объединение с каждым поиском, но расширяет выбор, чтобы включить столбцы всех связанных таблиц. Однако этот подход имеет оговорку.
Соединения имеют потенциал для умножения числа строк в запросе. Когда вы выполняете объединение по внешнему ключу или по отдельности, количество строк не увеличивается. Тем не менее, многие-ко-многим соединениям не имеют этой гарантии. Таким образом, Django ограничивает select_related
отношениями, которые неожиданно не приведут к массивному соединению.
"join in python" для prefetch_related
немного тревожит, тогда это должно быть. Он создает отдельный запрос для каждой связанной таблицы. Он фильтрует каждую из этих таблиц предложением WHERE IN, например:
SELECT "credential"."id",
"credential"."uuid",
"credential"."identity_id"
FROM "credential"
WHERE "credential"."identity_id" IN
(84706, 48746, 871441, 84713, 76492, 84621, 51472);
Вместо того, чтобы выполнять одно соединение с потенциально слишком большим количеством строк, каждая таблица разделяется на отдельный запрос.
Ответ 3
prefetch_related
используется для данных предварительной выборки для данных отношения "многие ко многим" и "многие к одному".
select_related
предназначен для выбора данных из одного значения отношения. Оба они используются для извлечения данных из их отношений из модели. Например, вы строите модель и модель, которая связана с другими моделями. Когда приходит запрос, вы также запрашиваете данные об их отношениях, и у Django есть очень хорошие механизмы для доступа к данным из их отношений, например, book.author.name
, но когда вы повторяете список моделей для извлечения данных об их отношениях, Django создает каждый запрос для каждой отдельной информации об отношениях., Для преодоления этого у нас есть prefetchd_related
и selected_related