В чем разница между select_related и prefetch_related в Django ORM?

168

В Django Doc,

select_related() "следует" за связями внешнего ключа, выбирая дополнительные данные связанного объекта при выполнении своего запроса.

prefetch_related() выполняет отдельный поиск для каждого отношения и выполняет "соединение" в Python.

Что это значит под "объединением в python"? Может кто-нибудь проиллюстрировать примером?

select_related я понимаю, для связи с внешним ключом используйте select_related; и для отношения M2M используйте prefetch_related. Это правильно?

  • 0
    Выполнение объединения в python означает, что соединение не произойдет в базе данных. При выборе select_related ваше соединение происходит в базе данных, и вы переносите только один запрос к базе данных. С prefetch_related вы будете выполнять два запроса, а затем результаты будут «объединены» ORM, так что вы все равно сможете набирать object.related_set
  • 1
    В качестве сноски Тимми О'Махони может также объяснить свои различия, используя обращения к базе данных: ссылка
Теги:
django-models
django-orm

2 ответа

270
Лучший ответ

Ваше понимание в основном верно. Вы используете 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 имеют приличную долю памяти, это также может быть рассмотрено.

  • 3
    что быстрее, хотя?
  • 9
    select_related - это один запрос, а prefetch_related - два, поэтому первый - быстрее. Но select_related не поможет вам для ManyToManyField
Показать ещё 5 комментариев
12

Оба метода достигают той же цели, чтобы отказаться от ненужных запросов 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);

Вместо того, чтобы выполнять одно соединение с потенциально слишком большим количеством строк, каждая таблица разделяется на отдельный запрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню