Получать несколько раз или фильтровать id__in на наборе запросов, что эффективнее в django?

Какой из следующих вариантов более эффективен в django, если длина ids не очень большая?

items = [item for item in queryset.filter(id__in=ids)]
items = [queryset.get(id=id) for id in ids]

Если мы хотим измерить эффективность, то сначала мы должны подумать о запросе. Потому что оба выполняют вычисление списка, и мы не имеем к этому никакого отношения.

Теперь, если мы перейдем к queryset.filter и queryset.get и попытаемся понять эквивалентный SQL запрос.

  • Количество запросов к SQL: Для filter будет только один запрос, но для get будет n количество запросов. Даже если n будет 2, filter будет эффективнее, чем get.
  • Эффективность выполнения: Этот показатель также зависит от SQL запроса. Поскольку, как говорит реализация get, она будет создавать запрос и выполнять его каждый раз. Но filter будет создавать один запрос и выполнять его один раз. А транснациональное выполнение (выполнение запроса к БД) всегда медленнее, чем выполнение запроса в коде.

Вы можете увидеть SQL-запрос queryset.filter, распечатав его атрибут query.

Я думаю, что filter более эффективен, чем get в данном случае.

Запрос, использующий .filter(…), будет более эффективным: он выполнит один запрос к базе данных, тогда как решение с .get(…) будет выполнять n запросов к базе данных, причем n - количество элементов в ids.

Хотя запрос .filter(…) может занять немного больше времени, построение запроса, отправка запроса в базу данных, декодирование запроса, построение плана выполнения и отправка результата обратно будут выполнены один раз при использовании .filter(…), в то время как для решения .get(…) это будет сделано n раз.

Обратите внимание, что эти два варианта семантически не эквивалентны: .filter(…) получит все элементы, для которых существует идентификатор id, решение .get(…), с другой стороны, выдаст ошибку, если не сможет найти идентификатор.

В случае, если количество возвращаемых элементов огромно, вам следует не хранить их в списке, а работать с .iterator(…) [Django-doc] для обработки элементов по частям, так:

for item in queryset.filter(id__in=ids).iterator():
    # do something …

Если бы вы хранили элементы в списке, это нарушило бы цель итератора, поскольку тогда все объекты будут "живыми" в одно и то же время, и поэтому должно быть достаточно памяти, чтобы хранить их в памяти

Вернуться на верх