Django множественные аннотации замедляют запрос - PullRequest
2 голосов
/ 07 января 2020

Я выполнял отладку с помощью django debug_toolbar, если я использую более одного аннотации в запросе, тогда Django потребуется много времени для получения результатов запроса.

class Project_First(models.Model):
   project_first_results_M2M = models.ManyToManyField(Project_First_Results)

class Project_Second(models.Model):
   project_second_results_M2M = models.ManyToManyField(Project_Second_Results)

class Project(models.Model):
    project_first_M2M = models.ManyToManyField(Project_First)
    project_second_M2M = models.ManyToManyField(Project_Second)
  • Я пытаюсь сосчитать все объекты, присутствующие в project_first_results_M2M всех project_first_M2M объектов.

ie, давайте предположим, что project_first_M2M имеет 3 объекта Project_First, и я хочу подсчитать все project_first_results_M2M объектов, присутствующих во всех 3 из них.

Project.objects.all().annotate(first_res_count=Count('project_first_M2M__project_first_results_M2M',distinct=True))
  • Выше Query работает нормально, и для получения результатов потребуется 80 мс. Но проблема возникает, когда я пытаюсь добавить дополнительный annotate к запросу.
Project.objects.all().annotate(first_res_count=Count('project_first_M2M__project_first_results_M2M',distinct=True)).annotate(second_res_count=Count('project_second_M2M__project_second_results_M2M',distinct=True))
  • Для получения результатов потребуется почти 4000 мс.

И project_second_M2M, и project_first_M2M содержат одинаковые поля и одинаковое количество объектов. Я даже попытался в обратном случае к вышеупомянутому запросу, и запрос замедляется только тогда, когда я добавляю дополнительные annotate.

  • . Есть ли какое-либо быстрое и альтернативное решение для достижения того же самого в гораздо более эффективной форме? возможно с необработанными sql запросами.
  • Я хочу подсчитать все объекты project_first_results_M2M всех project_first_M2M объектов в каждом Project объекте и аналогично для project_second_results_M2M

Ответы [ 2 ]

2 голосов
/ 07 января 2020

Возможно, вы можете использовать prefetch related:

Project.objects.prefetch_related('project_first_M2M__project_first_results_M2M', 'project_second_M2M__project_second_results_M2M').annotate(first_res_count=Count('project_first_M2M__project_first_results_M2M',distinct=True)).annotate(second_res_count=Count('project_second_M2M__project_second_results_M2M',distinct=True))
0 голосов
/ 07 января 2020

Если вы аннотируете несколько разных объединений, вы генерируете большое количество объединений (в этом примере всего четыре), которые действительно «взорвутся», таблица решений. Но, конечно, нет смысла выполнять такой запрос. Если вы выполните одно вложенное JOIN, то это приведет к двум JOIN, и вы примете во внимание все записи, которые были получены. - Williem

Решение, предложенное Matthew Schinckel при Django 1.11 Аннотирование агрегата подзапроса с использованием трюка подзапроса для получения результатов гораздо более быстрым и оптимизированным способом.

...